I have created a chat using SignalR2. The client and server itself works fine. Now, I'm trying to implement a 'users online' function. The server code seems about right, but I'm struggling to make the client receive the data that the server pushes back to the client.
This is the server code below:
public static List<string> Users = new List<string>();
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
Clients.All.addMessage(name, message);
}
public void SendUserList(List<string> users)
{
var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>();
context.Clients.All.updateUserList(users);
}
public override Task OnConnected()
{
string clientId = GetClientId();
//if (Users.IndexOf(clientId) == -1)
//{
Users.Add(clientId);
//}
SendCount(Users.Count);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
System.Diagnostics.Debug.WriteLine("Disconnected");
SendCount(Users.Count);
return base.OnDisconnected(stopCalled);
}
private string GetClientId()
{
string clientId = "";
if (Context.QueryString["clientId"] != null)
{
// clientId passed from application
clientId = this.Context.QueryString["clientId"];
}
if (string.IsNullOrEmpty(clientId.Trim()))
{
clientId = Context.ConnectionId;
}
return clientId;
}
public void SendCount(int count)
{
// Call the addNewMessageToPage method to update clients.
var context = GlobalHost.ConnectionManager.GetHubContext<chatHub>();
context.Clients.All.updateUsersOnlineCount(count);
}
Below is the client code for connecting / receiving messages:
public static async void ConnectAsync(RadChat ChatInternal)
{
ChatInternal.Author = new Author(null, Varribles.Agent);
var querystringData = new Dictionary<string, string>();
querystringData.Add("clientId", Varribles.Agent);
Connection = new HubConnection(ServerURI, querystringData);
HubProxy = Connection.CreateHubProxy("chatHub");
//Handle incoming event from server: use Invoke to write to console from SignalR's thread
HubProxy.On<string, string>("AddMessage", (name, message) =>
ChatInternal.Invoke((Action)(() =>
Backend.GET.Messages(ChatInternal)
)));
try
{
await Connection.Start();
Backend.GET.Messages(ChatInternal);
}
catch (System.Net.Http.HttpRequestException)
{
//No connection: Don't enable Send button or show chat UI
return;
}
}
Now, my question is, how can I retrieve the 'Users' list from the server?
Thanks in advance
Related
SignalR core is very new, so the docs detail how to use about it are very rare.
I've done the tutorial from Microsoft and successfully sent messages to all the clients. Now I want to send for specific user, with
public Task SendPrivateMessage(string user, string message, string to)
{
return Clients.User(to).SendAsync("ReceiveMessage", user, message);
}
the "to" value is the ConnectionID I got from the
public override Task OnConnectedAsync()
{
Console.WriteLine("New ID Connected: " + Context.ConnectionId);
return base.OnConnectedAsync();
}
Here is my client:
public async void InitSignalRAsync()
{
ChatMessage mess = new ChatMessage();
hubConnection = new HubConnectionBuilder().WithUrl("http://localhost:5000/chatHub").Build();
await hubConnection.StartAsync();
hubConnection.On<string, string>("ReceiveMessage", async (user, message) =>
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
mess.user = user;
mess.message = message;
Messages.Add(mess);
});
});
}
private void Send_Click(object sender, RoutedEventArgs e)
{
hubConnection.InvokeAsync("SendPrivateMessage", User.Text, Text.Text, To.Text);
}
The console logs nothing error so I guess it did send but why can't I receive it?
For send to client by client's connectionId you should use of Clients.Client(coonectionId)
,Clients.User() is for send to uniqe user by user's Id.to do send message by user id you can try as follows:
-create a CustomUserIdProvider:
public class CustomUserIdProvider: IUserIdProvider
{
public virtual string GetUserId(HubConnectionContext connection)
{
//get current user id by httpcontext
}
}
and then in startup.cs:
services.AddSignalR();
services.AddSignalRCore();
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
now in your hub you can send message by user id:
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
for more info go to this link
I want to send real-time notification in ASP.NET Boilerplate. Notification is saving successfully in Abp.NotificationSubscription table on subscription. When I publish the notification, the notification got saved in the Abp.Notification table but it is not displayed to the user in real-time.
My server-side code:
public async Task<RegisterOutput> Register(RegisterInput input)
{
public async Task<RegisterOutput> Register(RegisterInput input)
{
var user = await _userRegistrationManager.RegisterAsync(
input.Name,
input.Surname,
input.EmailAddress,
input.UserName,
input.Password,
true
);
_notificationSubscriptionManager.SubscribeToAllAvailableNotifications(user.ToUserIdentifier());
await _appNotifier.WelcomeToTheApplicationAsync(user);
var notification = _userNotificationManager.GetUserNotifications(user.ToUserIdentifier());
await _realTimeNotifier.SendNotificationsAsync(notification.ToArray());
// ...
}
}
_appNotifier implements WelcomeToTheApplicationAsync(user):
public async Task WelcomeToTheApplicationAsync(User user)
{
await _notificationPublisher.PublishAsync(
AppNotificationName.WelcomeToTheApplication,
new SendNotificationData("Naeem", "Hello I have sended this notification to you"),
severity: NotificationSeverity.Success,
userIds: new[] { user.ToUserIdentifier() }
);
}
SendNotificationData inherits from NotificationData:
public class SendNotificationData : NotificationData
{
public string name { get; set; }
public string message { get; set; }
public SendNotificationData(string _name, string _message)
{
name = _name;
message = _message;
}
}
I want to sent welcome notification to the user when he registers itself by using the register link on the login page. In the CloudbaseLine.Application project ... we have AccountAppService which contain the code for registering the user and sending notification to it after successful registration but the problem is notification go saved in Abp.NotificationSubscriptionTable and UserNotificationTable but user cannot received them.
public async Task<RegisterOutput> Register(RegisterInput input)
{
var user = await _userRegistrationManager.RegisterAsync(
input.Name,
input.Surname,
input.EmailAddress,
input.UserName,
input.Password,
true
);
_notificationSubscriptionManager.SubscribeToAllAvailableNotifications(user.ToUserIdentifier());
await _appNotifier.WelcomeToTheApplicationAsync(user);
var notification = _userNotificationManager.GetUserNotifications(user.ToUserIdentifier());
await _realTimeNotifier.SendNotificationsAsync(notification.ToArray());
// ...
}
public async Task WelcomeToTheApplicationAsync(User user)
{
await _notificationPublisher.PublishAsync(
AppNotificationName.WelcomeToTheApplication,
new SendNotificationData("Naeem", "Hello I have sended this notification to you"),
severity: NotificationSeverity.Success,
userIds: new[] { user.ToUserIdentifier() }
);
}
The user is not connected to the SignalR hub in the Register method.
One way to handle that is to enqueue a background job:
await _backgroundJobManager.EnqueueAsync<WelcomeNotificationJob, UserIdentifier>(
user.ToUserIdentifier(),
delay: TimeSpan.FromSeconds(5)
);
public class WelcomeNotificationJob : BackgroundJob<UserIdentifier>, ITransientDependency
{
private readonly IRealTimeNotifier _realTimeNotifier;
private readonly IUserNotificationManager _userNotificationManager;
public WelcomeNotificationJob(
IRealTimeNotifier realTimeNotifier,
IUserNotificationManager userNotificationManager)
{
_realTimeNotifier = realTimeNotifier;
_userNotificationManager = userNotificationManager;
}
[UnitOfWork]
public override void Execute(UserIdentifier args)
{
var notifications = _userNotificationManager.GetUserNotifications(args);
AsyncHelper.RunSync(() => _realTimeNotifier.SendNotificationsAsync(notifications.ToArray()));
}
}
Don't forget to register data formatters for custom notification data types, on the client side:
abp.notifications.messageFormatters['CloudBaseLine.Notification.SendNotificationData'] = function (userNotification) {
return userNotification.notification.data.message;
}
I am trying to use web socket with my bot to communicate with the server. But on run time it throws the System.NullReferenceException. I am running socket in background on a different thread so that it does not interfear with the bot.
I am using WebsocketSharp library.
First message comes in just fine but on second message it throws exception at following line in HumanCollaboratorDialog class.
await context.PostAsync(e.Data);
My Socket Stream Class is as following:
public static class SocketStream
{
public static WebSocket ws;
private static List<string> serverMsg = new List<string>();
public static void initializeSocket()
{
ws = new WebSocket("ws://Some IP:8080/human-collaborator/data");
Debug.WriteLine("****** INITIALIZED SOCKET (should happen only once) *****");
Task.Run(() => startSocketStream());
}
private static void startSocketStream()
{
int attempts = 0;
while (!ws.IsAlive)
{
try
{
attempts++;
ws.Connect();
}
catch (WebSocketException)
{
Debug.WriteLine("Connection attempts: " + attempts.ToString());
}
}
ws.OnOpen += (sender, args) =>
{
Debug.WriteLine("# SOCKET OPENED");
};
ws.OnError += (sender, args) =>
{
Debug.WriteLine("# SOME ERROR OCCURED");
};
ws.OnClose += (sender, args) =>
{
Debug.WriteLine("# SOCKET CLOSED");
};
}
}
I am calling the initializeSocket() method in Global.asx to run it on application level
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
SocketStream.initializeSocket();
}
}
My HumanCollaboratorDialog class is as following:
[Serializable]
public class HumanCollaboratorDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
SocketStream.ws.OnMessage += async (sender, e) =>
{
try
{
await context.PostAsync(e.Data);
}
catch (HttpRequestException ex)
{
throw ex;
}
};
Thread.Sleep(500);
string output = message.Text;
SocketStream.ws.Send(output);
Thread.Sleep(500);
context.Wait(MessageReceivedAsync);
}
}
My MessagesController has following POST method:
public virtual async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new HumanCollaboratorDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Neithet e.Data nor context is empty. I think problem is with socket connection or may be i am doing something wrong in SocketStream class. following is the image
Your bot is a web service. Messages are sent to the service by the client (a web page, an application, another service, etc.) and received in the MessagesController's Post method. There's no need to have the socket code on the server for what you're trying to do. Web Sockets are useful for receiving messages on a client from the bot via a Direct Line connection.
Here is an example of using the Bot Framework's Direct Line Client and creating a web socket connection. Notice how the web socket is created from a conversation's StreamUrl:
DirectLineClientCredentials creds = new DirectLineClientCredentials(directLineSecret);
DirectLineClient directLineClient = new DirectLineClient(creds);
Conversation conversation = await directLineClient.Conversations.StartConversationAsync();
using (var webSocketClient = new WebSocket(conversation.StreamUrl))
{
webSocketClient.OnMessage += WebSocketClient_OnMessage;
webSocketClient.Connect();
while (true)
{
string input = Console.ReadLine().Trim();
if (input.ToLower() == "exit")
{
break;
}
else
{
if (input.Length > 0)
{
Activity userMessage = new Activity
{
From = new ChannelAccount(fromUser),
Text = input,
Type = ActivityTypes.Message
};
await directLineClient.Conversations.PostActivityAsync(conversation.ConversationId, userMessage);
}
}
}
}
private static void WebSocketClient_OnMessage(object sender, MessageEventArgs e)
{
// avoid null reference exception when no data received
if (string.IsNullOrWhiteSpace(e.Data))
{
return;
}
var activitySet = JsonConvert.DeserializeObject<ActivitySet>(e.Data);
var activities = from x in activitySet.Activities
where x.From.Id == botId
select x;
foreach (Activity activity in activities)
{
Console.WriteLine(activity.Text);
}
}
This is from a console application that is using the Direct Line to communicate with the Bot and is listening for messages using web sockets here:
https://github.com/Microsoft/BotBuilder-Samples/tree/master/CSharp/core-DirectLineWebSockets
I want to connect to signalr with a client thats on a different pc. This means i wont be using localhost. I already made a simple networkdiscovery to get the correct ip address but it seems signalr does not allow remote clients to connect even though I already use CorsOptions.AllowAll.
class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration
{
#if DEBUG
EnableDetailedErrors = true
#else
EnableDetailedErrors = false
#endif
};
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR(hubConfiguration);
}
}
Iam using duality which is a 2d game engine. Here is the server:
public class SignalRServer : Component, ICmpInitializable
{
private IDisposable _signalRServer;
public int _port { get; set; } = 8080;
public void StopServer()
{
if (_signalRServer != null)
_signalRServer.Dispose();
}
public void OnInit(InitContext context)
{
if (context == InitContext.Activate && DualityApp.ExecContext == DualityApp.ExecutionContext.Game)
{
var networkDiscovery = new NetworkDiscovery(_port, "TestGame"); //Network discovery to get the ip adres of the server if one is found
IPEndPoint ipEndPoint;
if (networkDiscovery.LookForServer(out ipEndPoint))
{
try
{
ConnectToServer(ipEndPoint).Wait();
Debug.WriteLine($"Connection established to {ipEndPoint}");
}
catch (Exception ex)
{
Debug.WriteLine("Could not find server");
}
}
else //No server was found so we create one
{
Debug.WriteLine("Starting signalR server");
string url = $"http://*:{_port}"; //To test go to http://localhost:8080/signalr/hubs
networkDiscovery.Start();
_signalRServer = WebApp.Start<Startup>(url);
}
}
}
private async Task ConnectToServer(IPEndPoint ipEndPoint)
{
var hubConnection = new HubConnection($"http://{ipEndPoint}/");
IHubProxy hubProxy = hubConnection.CreateHubProxy(nameof(MyHub));
hubProxy.On<string, string>(nameof(MyHub.Send), (name, message) =>
{
Debug.WriteLine("Incoming data: {0} {1}", name, message);
});
ServicePointManager.DefaultConnectionLimit = 10;
await hubConnection.Start();
}
public void OnShutdown(ShutdownContext context)
{
StopServer();
}
}
And the hub:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
public override Task OnConnected()
{
Debug.WriteLine("Client connected: " + Context.ConnectionId);
Send("Server", $"Client with id {Context.ConnectionId} has connected");
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
Debug.WriteLine("Client disconnected: " + Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
}
There is a need to receive user data using a token. Hello. There is a need to receive user data using a token. I have a web api + websockets, websockets connection via a web browser.
var webSocket = new WebSocket(handlerUrl);
//Open connection handler.
webSocket.onopen = function () {
webSocket.send("{\"type\":\"LOGIN\",\"access_token\":\"Bearer HIDDEN\"}");
};
Once connected, I immediately send token.
On the server side, it looks as follows:
public class SocketClientController: ApiController
{
public HttpResponseMessage Get ()
{
HttpContext.Current.AcceptWebSocketRequest (new WebSocketHandler ());
return Request.CreateResponse (HttpStatusCode.SwitchingProtocols);
}
<miss>
Socket class:
<miss>
private void Login(string access_token)
{
// here i want get user info
}
public override void OnMessage(string input)
{
dynamic data = JObject.Parse(input);
switch ((string)data.type)
{
case "LOGIN":
Login((string)data.access_token);
break;
}
}
I use Identity, a variant with a token when you first received from the client suits me the data. Tell me how you can get the user input without going through the login and password, and use [Authorize].
Sorry for my english.
I decided my task! Below is the code:
public class MachineKeyProtector : Microsoft.Owin.Security.DataProtection.IDataProtector
{
private readonly string[] _purpose =
{
typeof(OAuthAuthorizationServerMiddleware).Namespace,
"Access_Token",
"v1"
};
public byte[] Protect(byte[] userData)
{
throw new NotImplementedException();
}
public byte[] Unprotect(byte[] protectedData)
{
return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
}
}
Use:
var secureDataFormat = new TicketDataFormat(new Providers.MachineKeyProtector());
AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);
var userId = ticket.Identity.GetUserId();