How to keep track of established connections using WebSockets

霸气de小男生 提交于 2019-12-30 09:35:14

问题


I am trying to get a real-time chat service for cross-platform devices to life. The problem is that System.Net.WebSockets namespace doesn't allow me directly to keep track of an established connection. I could take a sessionID of the current connection but how can I say perform the following action await socket.SendAsync(buffer, WebSocketMessageType.Text, CancellationToken.None) for a specific client?

If I could use Microsoft.WebSockets, I would have the possibility to create a WebSocketCollection() and do something like client.Send(message), but I can't send an ArraySegment<byte[]> through it. I also think this is more intended for AJAX clients and websites and this stuff.

I now 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 can I extend my project, save the sessions/connections and call a specific user connection in order to send it a message?


回答1:


Usually, you do not do something like that. The WebSocket should be just and endpoint to another layer or sub-system. For example, an event-driven architecture. On connection, you should start the handler, gather data from the connected socket like the cookie, the URL or whatever, and notify something else that such user with that cookie/url is connected in that socket.

About your code:

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

This would be a way of keep track of your users in a small project, but I recommend you to plug the WebSocket to another message 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:


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

Thanks @Jonesy for making me aware of this Microsoft library!

edit:

SignalR doesn't work properly on non-"IIS Express" mode. "Local IIS" causes the whole project to fail.

edit:

SignalR works now. A rewrite rule made it impossible to get the proper path.



来源:https://stackoverflow.com/questions/26263306/how-to-keep-track-of-established-connections-using-websockets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!