[英]Using WebSockets with 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){}
...
}
不幸的是,AcceptWebSocketRequest不再接受WebSocketHandler,而是它的新签名是......
public void AcceptWebSocketRequest(Func<AspNetWebSocketContext, Task> userFunc)
有没有人在ASP.NET Web API应用程序中有最新的实现原始websockets的链接或快速示例?
更新:在我和同事进行了一些研究之后,我们得出结论,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);
}
}
}
}
注意:显然错误检查和CancellationToken的正确使用留给读者练习。
这是一个较老的问题,但我想补充一点。
事实证明,你可以使用它,我不知道他们为何如此“隐藏”它。 如果有人可以向我解释这门课有什么问题,或者我在这里做的事情是某种“禁止”或“糟糕的设计”,那会很好。
如果我们查看Microsoft.Web.WebSockets.WebSocketHandler
,我们会发现这个公共方法:
[EditorBrowsable(EditorBrowsableState.Never)]
public Task ProcessWebSocketRequestAsync(AspNetWebSocketContext webSocketContext);
这个方法对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);
}
}
然后在我的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;
}
}
这完全没问题。 OnMessage被触发,并回到我的JavaScript实例化的WebSocket ...
我找到了这个例子 :
示例代码(从帖子转载):
public class WSHandler : IHttpHandler
{
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;
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.UTF8.GetString(
buffer.Array, 0, result.Count);
userMessage = "You sent: " + userMessage + " at " +
DateTime.Now.ToLongTimeString();
buffer = new ArraySegment<byte>(
Encoding.UTF8.GetBytes(userMessage));
await socket.SendAsync(
buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}
else
{
break;
}
}
}
}
基于Tony的答案与更清晰的任务处理共享我的代码。 此代码大约每秒发送当前UTC时间:
public class WsTimeController : ApiController
{
[HttpGet]
public HttpResponseMessage GetMessage()
{
var status = HttpStatusCode.BadRequest;
var context = HttpContext.Current;
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(ProcessRequest);
status = HttpStatusCode.SwitchingProtocols;
}
return new HttpResponseMessage(status);
}
private async Task ProcessRequest(AspNetWebSocketContext context)
{
var ws = context.WebSocket;
await Task.WhenAll(WriteTask(ws), ReadTask(ws));
}
// MUST read if we want the socket state to be updated
private async Task ReadTask(WebSocket ws)
{
var buffer = new ArraySegment<byte>(new byte[1024]);
while (true)
{
await ws.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
if (ws.State != WebSocketState.Open) break;
}
}
private async Task WriteTask(WebSocket ws)
{
while (true)
{
var timeStr = DateTime.UtcNow.ToString("MMM dd yyyy HH:mm:ss.fff UTC", CultureInfo.InvariantCulture);
var buffer = Encoding.UTF8.GetBytes(timeStr);
if (ws.State != WebSocketState.Open) break;
var sendTask = ws.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
await sendTask.ConfigureAwait(false);
if (ws.State != WebSocketState.Open) break;
await Task.Delay(1000).ConfigureAwait(false); // this is NOT ideal
}
}
}
那么使用SignalR 2怎么样?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.