将WebSockets与ASP.NET Web API一起使用

Ton*_*ony 21 c# websocket asp.net-mvc-4 signalr asp.net-web-api

在ASP.NET Web API应用程序中使用原始websockets的首选方法是什么?

我们想在ASP.NET Web API应用程序的几个接口上使用二进制 WebSockets.我很难确定应该如何完成这项工作,因为.NET似乎存在多个在线冲突和/或过时的实现.

有这似乎是ASP.NET这样的例子一个,但我认为必须有在Web API框架内使用WebSockets的手段.据我所知,你可以在WebAPI中使用Signalr.

以为使用Microsoft.AspNet.SignalR.WebSockets.WebSocketHandler会起作用,但我不确定如何将WebSocketHandler链接到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){}

    ...
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,AcceptWebSocketRequest不再接受WebSocketHandler,而是它的新签名是......

public void AcceptWebSocketRequest(Func<AspNetWebSocketContext, Task> userFunc)
Run Code Online (Sandbox Code Playgroud)

有没有人在ASP.NET Web API应用程序中有最新的实现原始websockets的链接或快速示例?

Ton*_*ony 14

更新:在我和同事进行了一些研究之后,我们得出结论,WebSocketHandler类似乎并不打算在SignalR的内部进程之外使用.因为没有明显的手段来利用与SignalR隔离的WebSocketHandler.这很不幸,因为我发现它的接口比System.Web/System.Net接口略高一些.此外,下面描述的方法使用HttpContext,我认为应该避免使用它.

因此,我们计划采用类似于Mrchief所示的方法,但具有更多的Web API风格.像这样......(注意:我们的套接字是只写的,但是我发现你必须执行你希望WebSocket.State正确更新的读操作.

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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:显然错误检查和CancellationToken的正确使用留给读者练习.


Ren*_*ers 12

这是一个较老的问题,但我想补充一点.
事实证明,你可以使用它,我不知道他们为何如此"隐藏"它.如果有人可以向我解释这门课有什么问题,或者我在这里做的事情是某种"禁止"或"糟糕的设计",那会很好.

如果我们查看Microsoft.Web.WebSockets.WebSocketHandler,我们找到这个公共方法:

[EditorBrowsable(EditorBrowsableState.Never)]
public Task ProcessWebSocketRequestAsync(AspNetWebSocketContext webSocketContext);
Run Code Online (Sandbox Code Playgroud)

这个方法对intellisense是隐藏的,但是它存在,并且可以在没有编译错误的情况下调用.
我们可以使用此方法来获取我们需要在AcceptWebSocketRequest方法中返回的任务.看一下这个:

public class MyWebSocketHandler : WebSocketHandler
{
    private static WebSocketCollection clients = new WebSocketCollection();

    public override void OnOpen()
    {
        clients.Add(this);
    }

    public override void OnMessage(string message)
    {
        Send("Echo: " + message);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的API控制器中:

public class MessagingController : ApiController
{
    public HttpResponseMessage Get()
    {
        var currentContext = HttpContext.Current;
        if (currentContext.IsWebSocketRequest ||
            currentContext.IsWebSocketRequestUpgrading)
        {
            currentContext.AcceptWebSocketRequest(ProcessWebsocketSession);
        }

        return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
    }

    private Task ProcessWebsocketSession(AspNetWebSocketContext context)
    {
        var handler = new MyWebSocketHandler();
        var processTask = handler.ProcessWebSocketRequestAsync(context);
        return processTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

这完全没问题.OnMessage被触发,并回到我的JavaScript实例化的WebSocket ...

  • 此示例使用 Microsoft.Web.WebSockets,如果您查看 nuget 包 Microsoft.WebSockets(我知道这很令人困惑),它已不再开发,他们建议使用 SignalR。这又回到了最初的问题:如何在上面的示例中使用 SignalR.WebSocketHandler。根据我的研究,这是不容易完成的。 (2认同)