How to track established connections using WebSockets

I am trying to get a live chat service for cross-platform devices. The problem is that the System.Net.WebSockets namespace does not allow me to monitor the established connection. I can take the sessionID of the current connection, but how can I say do the following await socket.SendAsync(buffer, WebSocketMessageType.Text, CancellationToken.None) for a specific client?

If I could use Microsoft.WebSockets , I would be able to create a WebSocketCollection() and do something like client.Send(message) , but I cannot send an ArraySegment<byte[]> through it. I also think this is more for AJAX clients and websites, etc.

Now I have the following code snippet:

 public class WSHandler : IHttpHandler { event NewConnectionEventHandler NewConnection; public void ProcessRequest(HttpContext context) { if (context.IsWebSocketRequest) { context.AcceptWebSocketRequest(ProcessWSChat); } } public bool IsReusable { get { return false; } } private async Task ProcessWSChat(AspNetWebSocketContext context) { WebSocket socket = context.WebSocket; int myHash = socket.GetHashCode(); while (true) { ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]); WebSocketReceiveResult result = await socket.ReceiveAsync( buffer, CancellationToken.None); if (socket.State == WebSocketState.Open) { string userMessage = Encoding.ASCII.GetString( buffer.Array, 0, result.Count); userMessage = "You sent: " + userMessage + " at " + DateTime.Now.ToLongTimeString() + " from ip " + context.UserHostAddress.ToString(); buffer = new ArraySegment<byte>( Encoding.ASCII.GetBytes(userMessage)); await socket.SendAsync( buffer, WebSocketMessageType.Text, true, CancellationToken.None); } else { break; } } } } 

How to extend my project, save sessions / connections and call a specific user connection to send him a message?

+5
source share
2 answers

Usually you do not do anything like that. WebSocket must be fair and finite for another layer or subsystem. For example, event driven architecture. When connecting, you must start the handler, collect data from the connected socket, such as a cookie, URL or something else, and notify something else that such a user with this cookie / url is connected to this socket.

About your code:

  • Do not declare a read buffer inside a loop; you can reuse it.
  • WebSockets always sends text data as UTF8, not ASCII.

This will be a way to track your users in a small project, but I recommend connecting WebSocket to another messaging infrastructure like MassTransit:

 public class WSHandler : IHttpHandler { public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { if (context.IsWebSocketRequest) { context.AcceptWebSocketRequest(ProcessWSChat); } } static readonly ConcurrentDictionary<IPrincipal, WebSocket> _users = new ConcurrentDictionary<IPrincipal, WebSocket>(); private async Task ProcessWSChat(AspNetWebSocketContext context) { WebSocket socket = context.WebSocket; ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[4096]); //Identify user by cookie or whatever and create a user Object var cookie = context.Cookies["mycookie"]; var url = context.RequestUri; IPrincipal myUser = GetUser(cookie, url); // Or uses the user that came from the ASP.NET authentication. myUser = context.User; _users.AddOrUpdate(myUser, socket, (p, w) => socket); while (socket.State == WebSocketState.Open) { WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None) .ConfigureAwait(false); String userMessage = Encoding.UTF8.GetString(buffer.Array, 0, result.Count); userMessage = "You sent: " + userMessage + " at " + DateTime.Now.ToLongTimeString() + " from ip " + context.UserHostAddress.ToString(); var sendbuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMessage)); await socket.SendAsync(sendbuffer , WebSocketMessageType.Text, true, CancellationToken.None) .ConfigureAwait(false); } // when the connection ends, try to remove the user WebSocket ows; if (_users.TryRemove(myUser, out ows)) { if (ows != socket) { // whops! user reconnected too fast and you are removing // the new connection, put it back _users.AddOrUpdate(myUser, ows, (p, w) => ows); } } } private IPrincipal GetUser(HttpCookie cookie, Uri url) { throw new NotImplementedException(); } } 
+2
source

I think that reinventing the wheel would be very stupid, so I better use SignalR for this purpose.

Thanks to @Jonesy for learning about this Microsoft library!

change

SignalR does not work properly in IIS Express mode. Local IIS crashes the entire project.

change

SignalR is working now. The rewrite rule did not allow to get the correct path.

+1
source

Source: https://habr.com/ru/post/1204276/


All Articles