Tony Tony - 3 months ago 254
C# Question

Using WebSockets with ASP.NET Web API

What is the preferred method for using raw websockets in an ASP.NET Web API application?

We'd like to use binary WebSockets on a couple of our interfaces of our ASP.NET Web API application. I'm having a difficult time determining how this should be done as there seems to be several conflicting and/or out-dated implementations online for .NET.

There are examples which appear to be ASP.NET like this one, but I think there must be a means to use websockets within the Web API framework. As I know you can use Signalr within WebAPI.

I thought using Microsoft.AspNet.SignalR.WebSockets.WebSocketHandler would work, but I'm not sure how to link the WebSocketHandler to the Controller...

class MyServiceController : ApiController
{
[HttpGet]
public HttpResponseMessage SwitchProtocols (string param)
{
HttpContext currentContext = HttpContext.Current;
if (currentContext.IsWebSocketRequest ||
currentContext.IsWebSocketRequestUpgrading)
{
// several out-dated(?) examples would
// use 'new MySocketHandler' for ???
var unknown = new ????
currentContext.AcceptWebSocketRequest(unknown);
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
}
}

class MySocketHandler : WebSocketHandler
{
public MySocketHandler(): base(2048){}

...
}


Unfortunately, AcceptWebSocketRequest no longer accepts a WebSocketHandler, instead its new signature is...

public void AcceptWebSocketRequest(Func<AspNetWebSocketContext, Task> userFunc)


Does anyone have a link or a quick sample implementing raw websockets in ASP.NET Web API application that is up-to-date?

Answer

UPDATE: After a bit more research by myself and a coworker, we came to the conclusion that the WebSocketHandler class does not appear to be intended to be used outside of the internal processes of SignalR. As there is no obvious means to leverage WebSocketHandler isolated from SignalR. This is unfortunate as I find its interfaces slightly more high-level than the System.Web/System.Net interfaces. Moreover, the method described below makes use of HttpContext which I believe should be avoided.

As such we plan to take an approach similar to the one shown by Mrchief, but with a bit more Web API flavor. Like this...(NOTE: our socket is write-only, but I discovered you MUST perform read operations of you want WebSocket.State to get updated properly.

class MyServiceController : ApiController
{
    public HttpResponseMessage Get (string param)
    {
        HttpContext currentContext = HttpContext.Current;
        if (currentContext.IsWebSocketRequest || 
            currentContext.IsWebSocketRequestUpgrading)
        {
            currentContext.AcceptWebSocketRequest(ProcessWebsocketSession); 
            return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
        }   
    }

    private async Task ProcessWebsocketSession(AspNetWebSocketContext context)
    {
        var ws = context.WebSocket;

        new Task(() =>
        {
            var inputSegment = new ArraySegment<byte>(new byte[1024]);

            while (true)
            {
                // MUST read if we want the state to get updated...
                var result = await ws.ReceiveAsync(inputSegment, CancellationToken.None);

                if (ws.State != WebSocketState.Open)
                {
                    break;
                }
            }
        }).Start();

        while (true)
        {
            if (ws.State != WebSocketState.Open)
            {
                break;
            }
            else
            {
                byte[] binaryData = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe };
                var segment = new ArraySegment<byte>(binaryData);
                await ws.SendAsync(segment, WebSocketMessageType.Binary, 
                    true, CancellationToken.None);
            }
        }
    }
}

NOTE: Obviously error checking and proper usage of a CancellationToken is left as an exercise for the reader.