使用OWIN的Websockets

到目前为止我在网络api上使用Microsoft WebSockets的所有示例都使用IIS,实现是在get方法上将HTTP连接升级为websocket,并将websocket处理程序的实例传递给HTTPContext

public HttpResponseMessage Get() { if (HttpContext.Current.IsWebSocketRequest) { var noteHandler = new NoteSocketHandler(); HttpContext.Current.AcceptWebSocketRequest(noteHandler); } return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); } 

我想要实现的是在OWIN管道上做同样的事情。 我面临的问题是连接正在升级以使用Websockets,但它没有使用我的websocket处理程序。 我哪里错了? 请建议。

使用OwinContext的控制器(遵循Nancy使用OWIN的示例WebSockets ),

 public HttpResponseMessage Get() { IOwinContext owinContext = Request.GetOwinContext(); WebSocketAccept acceptToken = owinContext.Get("websocket.Accept"); if (acceptToken != null) { var requestHeaders = GetValue<IDictionary>(owinContext.Environment, "owin.RequestHeaders"); Dictionary acceptOptions = null; string[] subProtocols; if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) { acceptOptions = new Dictionary(); // Select the first one from the client acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim()); } acceptToken(acceptOptions, async wsEnv => { var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"]; var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"]; var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"]; var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"]; //should I pass the handler to an event? var handler = new NoteSocketHAndler(); }); } else { return new HttpResponseMessage(HttpStatusCode.BadRequest); } return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); } 

处理程序代码:

 using System; using Socket = Microsoft.Web.WebSockets; using Newtonsoft.Json; public class NoteSocketHandler : Socket.WebSocketHandler { private static Socket.WebSocketCollection connections = new Socket.WebSocketCollection(); public NoteSocketHandler() { } public override void OnOpen() { connections.Add(this); } public override void OnClose() { connections.Remove(this); } public override void OnMessage(string message) { ChatMessage chatMessage = JsonConvert.DeserializeObject(message); foreach (var connection in connections) { connection.Send(message); } } } 

我终于想出了如何解决这个问题。 您可以在下面找到代码,我也写了一个在OWIN上使用websockets的基本应用程序。

 using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Microsoft.Owin; namespace NoteApp.WebService.Controller { using System; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; using NoteApp.WebService.Handler; using WebSocketAccept = System.Action< System.Collections.Generic.IDictionary, // WebSocket Accept parameters System.Func< // WebSocketFunc callback System.Collections.Generic.IDictionary, // WebSocket environment System.Threading.Tasks.Task>>; using WebSocketCloseAsync = System.Func< int, // closeStatus string, // closeDescription System.Threading.CancellationToken, // cancel System.Threading.Tasks.Task>; using WebSocketReceiveAsync = System.Func< System.ArraySegment, // data System.Threading.CancellationToken, // cancel System.Threading.Tasks.Task< System.Tuple< // WebSocketReceiveTuple int, // messageType bool, // endOfMessage int>>>; // count // closeStatusDescription using WebSocketReceiveResult = System.Tuple; using WebSocketSendAsync = System.Func< System.ArraySegment, // data int, // message type bool, // end of message System.Threading.CancellationToken, // cancel System.Threading.Tasks.Task>; public class NoteController : ApiController { public HttpResponseMessage Get() { IOwinContext owinContext = Request.GetOwinContext(); WebSocketAccept acceptToken = owinContext.Get("websocket.Accept"); if (acceptToken != null) { var requestHeaders = GetValue>(owinContext.Environment, "owin.RequestHeaders"); Dictionary acceptOptions = null; string[] subProtocols; if (requestHeaders.TryGetValue("Sec-WebSocket-Protocol", out subProtocols) && subProtocols.Length > 0) { acceptOptions = new Dictionary(); // Select the first one from the client acceptOptions.Add("websocket.SubProtocol", subProtocols[0].Split(',').First().Trim()); } acceptToken(acceptOptions, ProcessSocketConnection); } else { return new HttpResponseMessage(HttpStatusCode.BadRequest); } return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); } private async Task ProcessSocketConnection(IDictionary wsEnv) { var wsSendAsync = (WebSocketSendAsync)wsEnv["websocket.SendAsync"]; var wsCloseAsync = (WebSocketCloseAsync)wsEnv["websocket.CloseAsync"]; var wsCallCancelled = (CancellationToken)wsEnv["websocket.CallCancelled"]; var wsRecieveAsync = (WebSocketReceiveAsync)wsEnv["websocket.ReceiveAsync"]; //pass the sendasync tuple and the cancellation token to the handler. The handler uses the sendasync method to send message. Each connected client has access to this var handler = new NoteSocketHandler(wsSendAsync, CancellationToken.None); handler.OnOpen(); var buffer = new ArraySegment(new byte[100]); try { object status; while (!wsEnv.TryGetValue("websocket.ClientCloseStatus", out status) || (int)status == 0) { WebSocketReceiveResult webSocketResultTuple = await wsRecieveAsync(buffer, CancellationToken.None); int count = webSocketResultTuple.Item3; handler.OnMessage(Encoding.UTF8.GetString(buffer.Array, 0, count)); } } catch (Exception ex) { Console.WriteLine(ex.Message); throw ex; } handler.OnClose(); await wsCloseAsync((int)WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None); } T GetValue(IDictionary env, string key) { object value; return env.TryGetValue(key, out value) && value is T ? (T)value : default(T); } } }