Signalr .Net Client Console application receive messages from hub only once - asp.net-mvc

I'm using Signalr .Net Client in my Console application to receive messages from the Signalr Hub which is a separate web application.
My console application is connecting to the hub correctly and receive message from the hub only once. Then the client method in the Signalr .Net client not getting called.
Once I stop the console application and run it, again it receive a message from the hub only once and nothing happens.
Here is my Hub Code
public override Task OnConnected()
{
try
{
var cType = Context.QueryString["type"];
var connectionId = Context.ConnectionId;
var connectedUserList = (from d in Users
where d.ClientType == cType
select d).ToList();
if (connectedUserList.Count > 0)
{
var conUser = connectedUserList.First<ConnectedUsers>();
conUser.ConnectionIds.Add(connectionId);
}
else
{
var newUser = new ConnectedUsers
{
ConnectionIds = new HashSet<string> {connectionId}
,
ClientType = cType
};
Users.Add(newUser);
}
}
catch (Exception ex)
{
).Error(ex);
}
return base.OnConnected();
}
And My .Net Client Connection
static void Main(string[] args)
{
SignalrHandler();
Console.ReadLine();
}
public static async void SignalrHandler()
{
var url = ConfigurationSettings.AppSettings["Url"] ?? #"http://localhost:1010/";
var querystringData = new Dictionary<string, string> { { "type", "WIN" } };
_hubConnection = new HubConnection(url, querystringData);
MarcolinMainProxy = _hubConnection.CreateHubProxy("MainHub");
MarcolinMainProxy.On<string>("sendAlert", type => InvokeMethod(type));
await _hubConnection.Start();
}
Client Method
private static void InvokeMethod(string type)
{
Console.WriteLine(String.Format("Recieved Message From Server On :{0}",System.DateTime.Now.ToString()));
Console.WriteLine("Message Received");
Console.ReadLine();
}
And This happens when I use an Invoke method with following line
MarcolinMainProxy.On<string>("sendAlert", type => InvokeMethod(type));
And when I use following line it works..
MarcolinMainProxy.On<string>("sendAlert", stock => Console.WriteLine("Symbol {0} Price {1}", "sd", "sdde"));

Check the following link
https://damienbod.com/2013/11/13/signalr-messaging-a-complete-client-with-a-console-application/
You have to change your code to
MarcolinMainProxy.On<string>("sendAlert", InvokeMethod);

Related

Connect to Office365 via backend service using OAuth2 in NON interactive way (bar initial setup)

I have a background service which reads & sends from a mailbox. It is created in a web ui, but after the schedule is created and mailbox set, it should run automatically, without further user prompt.
I have used the various combinations of the MSAL and both public and confidential clients (either would be acceptable as the server can maintain the client secret.
I have used the EWS client and got that working, but there is a note that the client_credentials flow won't work for IMAP/POP/SMTP.
I have a small console app working, but each time it runs, it needs to login interactively, and so long as I don't restart the application, it will keep authenticating, and I can call the AquireTokenSilently.
The Question
How can I make the MSAL save the tokens/data such that when it next runs, I can authenticate without user interaction again? I can store whatever is needed to make this work when the user authenticates, but I don't know what that should be nor how to reinstate it to make a new request, if the console app is restarted.
The Code
internal async Task<string> Test()
{
PublicClientApplication =
PublicClientApplicationBuilder.Create( "5896de31-e251-460c-9dc2-xxxxxxxxxxxx" )
.WithRedirectUri( "https://login.microsoftonline.com/common/oauth2/nativeclient" )
.WithAuthority( AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings["tenantId"] )
.Build();
//var scopes = new string[] { "email", "offline_access", "profile", "User.Read", "Mail.Read" };
var scopes = new string[] { "https://outlook.office.com/IMAP.AccessAsUser.All" };
var accounts = await PublicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
AuthenticationResult authResult;
if (firstAccount == null )
{
authResult = await PublicClientApplication.AcquireTokenInteractive( scopes ).ExecuteAsync();
}
else
{
//The firstAccount is null when the console app is run again
authResult = await PublicClientApplication.AcquireTokenSilent( scopes, firstAccount ).ExecuteAsync();
}
if(authResult == null)
{
authResult = await PublicClientApplication.AcquireTokenInteractive( scopes ).ExecuteAsync();
}
MailBee.Global.LicenseKey = "MN120-569E9E8D9E5B9E8D9EC8C4BC83D3-D428"; // (demo licence only)
MailBee.ImapMail.Imap imap = new MailBee.ImapMail.Imap();
var xOAuthkey = MailBee.OAuth2.GetXOAuthKeyStatic( authResult.Account.Username, authResult.AccessToken );
imap.Connect( "imap.outlook.com", 993 );
imap.Login( null, xOAuthkey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, null );
imap.SelectFolder( "INBOX" );
var count = imap.MessageCount.ToString();
return authResult.AccessToken;
}
It feels very much like a step missed, which can store the information to make subsequent requests and I would love a pointer in the right direction please.
When you create your PublicClientApplication, it provides you with the UserTokenCache.
UserTokenCache implements interface ITokenCache, which defines events to subscribe to token cache serialization requests as well as methods to serialize or de-serialize the cache at various formats.
You should create your own TokenCacheBuilder, which can store the tokens in file/memory/database etc.. and then use the events to subscribe to to token cache request.
An example of a FileTokenCacheProvider:
public abstract class MsalTokenCacheProviderBase
{
private Microsoft.Identity.Client.ITokenCache cache;
private bool initialized = false;
public MsalTokenCacheProviderBase()
{
}
public void InitializeCache(Microsoft.Identity.Client.ITokenCache tokenCache)
{
if (initialized)
return;
cache = tokenCache;
cache.SetBeforeAccessAsync(OnBeforeAccessAsync);
cache.SetAfterAccessAsync(OnAfterAccessAsync);
initialized = true;
}
private async Task OnAfterAccessAsync(TokenCacheNotificationArgs args)
{
if (args.HasStateChanged)
{
if (args.HasTokens)
{
await StoreAsync(args.Account.HomeAccountId.Identifier,
args.TokenCache.SerializeMsalV3()).ConfigureAwait(false);
}
else
{
// No token in the cache. we can remove the cache entry
await DeleteAsync<bool>(args.SuggestedCacheKey).ConfigureAwait(false);
}
}
}
private async Task OnBeforeAccessAsync(TokenCacheNotificationArgs args)
{
if (!string.IsNullOrEmpty(args.SuggestedCacheKey))
{
byte[] tokenCacheBytes = await GetAsync<byte[]>(args.SuggestedCacheKey).ConfigureAwait(false);
args.TokenCache.DeserializeMsalV3(tokenCacheBytes, shouldClearExistingCache: true);
}
}
protected virtual Task OnBeforeWriteAsync(TokenCacheNotificationArgs args)
{
return Task.CompletedTask;
}
public abstract Task StoreAsync<T>(string key, T value);
public abstract Task DeleteAsync<T>(string key);
public abstract Task<T> GetAsync<T>(string key);
public abstract Task ClearAsync();
}
And the MsalFileTokenCacheProvider:
public sealed class MsalFileTokenCacheProvider : MsalTokenCacheProviderBase
{
private string basePath;
public MsalFileTokenCacheProvider(string basePath)
{
this.basePath = basePath;
}
public override Task ClearAsync()
{
throw new NotImplementedException();
}
public override Task DeleteAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
string path = Path.Combine(basePath, key + ".json");
if (File.Exists(path))
File.Delete(path);
return Task.FromResult(true);
}
public override Task<T> GetAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
string path = Path.Combine(basePath, key + ".json");
if (File.Exists(path))
{
T value = JsonConvert.DeserializeObject<T>(File.ReadAllText(path));
return Task.FromResult(value);
}
else
return Task.FromResult(default(T));
}
public override Task StoreAsync<T>(string key, T value)
{
string contents = JsonConvert.SerializeObject(value);
string path = Path.Combine(basePath, key + ".json");
File.WriteAllText(path, contents);
return Task.FromResult(value);
}
}
So based on your code you will have:
PublicClientApplication =
PublicClientApplicationBuilder.Create( "5896de31-e251-460c-9dc2-xxxxxxxxxxxx" )
.WithRedirectUri( "https://login.microsoftonline.com/common/oauth2/nativeclient" )
.WithAuthority( AzureCloudInstance.AzurePublic, ConfigurationManager.AppSettings["tenantId"] )
.Build();
MsalFileTokenCacheProvider cacheProvider = new MsalFileTokenCacheProvider("TokensFolder");
cacheProvider.InitializeCache(PublicClientApplication.UserTokenCache);
//var scopes = new string[] { "email", "offline_access", "profile", "User.Read", "Mail.Read" };
var scopes = new string[] { "https://outlook.office.com/IMAP.AccessAsUser.All" };
// when you call the below code, the PublicClientApplication will use your token cache
//provider in order to get the required Account. You should also use the
//PublicClientApplication.GetAccountAsync(key) which will use the token cache provider for
//the specific account that you want to get the token. If there is an account you could
//just call the AcquireTokenSilent method. The acquireTokenSilent method will take care of the token expiration and will refresh if needed.
//Please bare in mind that in some circumstances the AcquireTokenSilent method will fail and you will have to use the AcquireTokenInteractive method again. //Example of this would be when the user changes password, or has removed the access to your Application via their Account.
var accounts = await PublicClientApplication.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
Please refer to the following documentation from Microsoft.
https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-net-token-cache-serialization

SqlDependency not working mvc app. Hits once

I am following the blog http://www.venkatbaggu.com/signalr-database-update-notifications-asp-net-mvc-usiing-sql-dependency/ to get a signalR push message out to connected clients.
My debugger never hits the onchange event.
my Global.asax.cs:
string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
protected void Application_Start()
{
// basic stuff
SqlDependency.Start(connString);
var repo = new Repositories.MarkerRepository();
repo.GetAllMarkers(); // to register the dependency
}
My MarkerRepository.cs:
readonly string _connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
private MarkersHub _mh = new MarkersHub(); // my signalr hub class
public IEnumerable<House> GetAllMarkers()
{
var markers = new List<House>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT * FROM [dbo].Houses", connection))
{
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
markers.Add(item: new House {
ID = (int)reader["ID"],
Name = (string)reader["Name"],
Code = reader["Code"] != DBNull.Value ? (string)reader["Code"] : "",
Latitude = Convert.ToDouble(reader["Latitude"]),
Longitude = Convert.ToDouble(reader["Longitude"])
});
}
}
}
return markers;
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
_mh.SendMarkers();
}
}
I have had a hit once but it was no change, only a notification for subscribe. I have read a lot about resubscribe, but when it hit this event the sql:
select * from sys.dm_qn_subscriptions
still returns no rows. Not on my db or master. So I think that there is an error in the blog post with the re-subscribe to the on change event? This sample https://msdn.microsoft.com/en-us/library/a52dhwx7(VS.80).aspx does unregister in the onchange event and calls the method which registers a new event. Can someone verify my assumption?
These were the values for the SqlNotificationEventArgs e in my event and told me that my query to depend on, was invalid.
SqlNotificationEventArgs.Type --> SqlNotificationType.Subscribe
SqlNotificationEventArgs.Info --> SqlNotificationInfo.Invalid
SqlNotificationEventArgs.Source --> SqlNotificationSource.Statement
The statement may not use the asterisk () or table_name. syntax to specify columns.
source https://msdn.microsoft.com/en-us/library/ms181122.aspx

SignalR: can't connect to local or any other ip address

I am trying to make SignalR server and client architecture in which i am able to connect to "http://localhost:8080" or http://127.0.0.1:8080/ but i am not able to connect my local ip address like "192. x.x.x" so what could be reason?
please help me i am also placing my code overhere...
public partial class WinFormsServer : Form
{
private IDisposable SignalR { get; set; }
const string ServerURI = "http://localhost:8080";
private void ButtonStart_Click(object sender, EventArgs e)
{
WriteToConsole("Starting server...");
ButtonStart.Enabled = false;
Task.Run(() => StartServer());
}
private void StartServer()
{
try
{
SignalR = WebApp.Start(ServerURI);
}
catch (TargetInvocationException)
{
WriteToConsole("Server failed to start. A server is already running on " + ServerURI);
//Re-enable button to let user try to start server again
this.Invoke((Action)(() => ButtonStart.Enabled = true));
return;
}
this.Invoke((Action)(() => ButtonStop.Enabled = true));
WriteToConsole("Server started at " + ServerURI);
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
}
I tried different solutions but could not find correct one.
Finally I found the issue that was only related to the permission.
Run your SignalR server application as administrator. It will start running on the local IP like 192.168.X.X:9090 and then your client application can connect this server from any other PC using this IP address.
class Program
{
static void Main(string[] args)
{
var url = $"http://{GetLocalIPAddress()}:8080";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine($"Server running at {{{url}}}");
Console.ReadLine();
}
}
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("Local IP Address Not Found!");
}
}
To get your local IP address you could use this function:
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("Local IP Address Not Found!");
}
If you want to use FQDN - Fully Qualified Domain Name, then you could use this function:
public static string GetLocalFQDN()
{
var props = IPGlobalProperties .GetIPGlobalProperties();
return props.HostName + (string.IsNullOrWhiteSpace(props.DomainName) ? "" : "." + props.DomainName);
}
After that you could use:
SignalR = WebApp.Start("http://" + GetLocalFQDN() + ":8080");
or
SignalR = WebApp.Start("http://" + GetLocalIPAddress() + ":8080");
I hope this helps.
Since you are using this source i used the same too.
-For FQDN, first create the function below.
public static string GetLocalFQDN()
{
var props = IPGlobalProperties.GetIPGlobalProperties();
return props.HostName + (string.IsNullOrWhiteSpace(props.DomainName) ? "" : "." + props.DomainName);
}
Then modify the const string ServerURI to:
string ServerURI =String.Concat("http://",GetLocalFQDN(),":8080");
-For LocalIPAdress, first create the function below which will the your local address:
public static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("Local IP Address Not Found!");
}
and change the string ServerURI =String.Concat("http://",GetLocalFQDN(),":8080"); to:
string ServerURI =String.Concat("http://",GetLocalIPAddress(),":8080");
Hope this helps you.
Note: The changes should be done in the WinFormsServer:Form class on the WinFormsServer project.

JavaFX WebView cookie

How do I get the cookie set by a connection to a webpage from JavaFX WebView. I want to use this cookie in order to open a seperate connection to the website after the originaol login.
Is there a way to do this and how?
Thank you for your time
I have a local "login.html" that does an AJAX call to my server to log in.
JavaFX application code:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Battleround client");
URL loginPageUrl = Main.class.getResource("/pages/login.html");
final WebView webview = new WebView();
final WebEngine webEngine = webview.getEngine();
webEngine.setJavaScriptEnabled(true);
webEngine.load(loginPageUrl.toString());
/*
* Alright, this piece of code might be hard to understand. Basically
* we're adding a JavaScript object that's actually a Java object. So we
* can call Java methods from JavaScript. And we're adding this
* javascript object as soon as the page has been fully loaded.
*/
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> ov, State t, State t1) {
if (t1 == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("java", new AuthenticationApplication());
}
}
});
StackPane root = new StackPane();
root.getChildren().add(webview);
primaryStage.setScene(new Scene(root, 400, 500));
primaryStage.show();
}
public class AuthenticationApplication {
public void start(String JSESSIONID) {
// From here on I start my game with the JSESSIONID from the login call.
}
}
My JavaScript code in the login.html:
function login(username, password) {
var xmlHttp = new XMLHttpRequest();
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "http://192.168.2.7:8888/Login";
var params = "username=" + userName + "&password=" + password;
xmlHttp.open("POST", url, true);
// Send the proper header information along with the request
xmlHttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
xmlHttp.setRequestHeader("Content-length", params.length);
xmlHttp.setRequestHeader("Connection", "close");
xmlHttp.withCredentials = "true";
xmlHttp.onreadystatechange = function() {// Call a function when the state changes.
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
if(xmlHttp.responseText == "login.successful") {
document.getElementById("loginForm").innerHTML = getResources("login.successful");
var setCookieHeader = xmlHttp.getResponseHeader('Set-Cookie');
java.start(setCookieHeader.split(";")[0].split("=")[1]); // Obtain the JSESSIONID, and send it to the java code. Warning: this code will mess up if there's other cookies.
} else {
document.getElementById("validationMessage").innerHTML = getResources(xmlHttp.responseText);
}
}
};
xmlHttp.send(params);
}
function getResources(key) {
var resources = {};
resources["login.already.logged.in"] = "You are already logged in! Log out first if you want to try again.";
resources["login.error"] = "The login failed because an error occurred, Sorry!";
resources["login.failed"] = "User name and password do not match.";
resources["login.successful"] = "Login succeeded.";
return resources[key];
}

c# server and android client,connectivity

i am trying to develop an application in c# which acts as a server for an android phone.i am using 32feet.net for bluetooth in c# and i have a server running in android, which simply sends a socket to server. the server running in pc need to listen the connection and display ,the status of connection. all these things are base for my project. the server code is as shown :
namespace testserver
{
class Program
{
static void Main(string[] args)
{
BluetoothClient bc = new BluetoothClient();
BluetoothDeviceInfo[] dev;
BluetoothDeviceInfo td=null;
Guid id = new Guid("{00112233-4455-6677-8899-aabbccddeeff}");
// Console.WriteLine(id.ToString());
// Console.Read();
dev = bc.DiscoverDevices();
foreach (BluetoothDeviceInfo d in dev)
{
if (d.DeviceName == "ST21i")//my phone name
{
td=d;
break;
}
}
try
{
BluetoothAddress addr = td.DeviceAddress;
BluetoothListener bl = new BluetoothListener(addr, id);
bl.Start();
if (bl.AcceptSocket() != null)
Console.WriteLine("Success");
}
catch (Exception e)
{
Console.WriteLine("Exception : "+e.Message);
Console.Read();
}
}
}
}
and here is my android code :
public class MainActivity extends Activity {
BluetoothAdapter adapter;
BluetoothDevice bd;
BluetoothSocket sock;
OutputStream ostr;
int REQUEST_ENABLE_BT;
String str="5C:AC:4C:DD:CC:0D";
private static final UUID id=UUID.fromString("00112233-4455-6677-8899- aabbccddeeff");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter=BluetoothAdapter.getDefaultAdapter();
if (!adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
Button b = (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "clicked button", Toast.LENGTH_LONG).show();
try
{
bd=adapter.getRemoteDevice(str); Toast.makeText(getApplicationContext(),"Server is running at "+bd.getName().toString()+"...", Toast.LENGTH_LONG).show();
sock=bd.createInsecureRfcommSocketToServiceRecord(id); sock.connect();
ostr=sock.getOutputStream();
ostr.write(0);
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
}
my problems are :
1) in pc i am getting an exception, the requested address is not valid in its context(so that server cant run )
2)in phone, the service discovery failed( because of unavailability of server)
how can i correct the server and run the program ?
i changed the bluetooth listener object's creation from
BluetoothListener bl = new BluetoothListener(addr, id); to
BluetoothListener bl = new BluetoothListener(id); and everything worked fine..

Resources