如何在.Net中优雅地关闭双向WebSocket
我有一个WebSocket服务器,它接受来自客户端的二进制数据流,并响应每4MB读取的另一个文本数据流。 服务器使用IIS 8和asp.net web api 。
服务器
public class WebSocketController : ApiController { public HttpResponseMessage Get() { if (!HttpContext.Current.IsWebSocketRequest) { return new HttpResponseMessage(HttpStatusCode.BadRequest); } HttpContext.Current.AcceptWebSocketRequest(async (context) => { try { WebSocket socket = context.WebSocket; byte[] requestBuffer = new byte[4194304]; int offset = 0; while (socket.State == WebSocketState.Open) { var requestSegment = new ArraySegment(requestBuffer, offset, requestBuffer.Length - offset); WebSocketReceiveResult result = await socket.ReceiveAsync(requestSegment, CancellationToken.None); if (result.MessageType == WebSocketMessageType.Close) { // Send one last response before closing var response = new ArraySegment(Encoding.UTF8.GetBytes("Server got " + offset + " bytes\n")); await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None); // Close await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); break; } offset += result.Count; if (offset == requestBuffer.Length) { // Regular response var response = new ArraySegment(Encoding.UTF8.GetBytes("Server got 4194304 bytes\n")); await socket.SendAsync(response, WebSocketMessageType.Text, true, CancellationToken.None); offset = 0; } } } catch (Exception ex) { // Log and continue } }); return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols); } }
c#客户端使用ClientWebSocket类连接到服务器并发送请求。 它创建一个任务,用于从服务器接收与请求发送并行运行的响应。 完成发送请求后,它CloseAsync
在套接字上调用CloseAsync
,然后等待Receive
任务完成。
客户
using System; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace WebSocketClient { class Program { static void Main(string[] args) { try { CallWebSocketServer().Wait(); } catch (Exception ex) { Console.WriteLine(ex); } } static async Task CallWebSocketServer() { using (ClientWebSocket socket = new ClientWebSocket()) { await socket.ConnectAsync(new Uri("ws://localhost/RestWebController"), CancellationToken.None); byte[] buffer = new byte[128 * 1024]; Task receiveTask = Receive(socket); for (int i = 0; i < 1024; ++i) { await socket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Binary, true, CancellationToken.None); } await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); receiveTask.Wait(); Console.WriteLine("All done"); } } static async Task Receive(ClientWebSocket socket) { try { byte[] recvBuffer = new byte[64 * 1024]; while (socket.State == WebSocketState.Open) { var result = await socket.ReceiveAsync(new ArraySegment(recvBuffer), CancellationToken.None); Console.WriteLine("Client got {0} bytes", result.Count); Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count)); if (result.MessageType == WebSocketMessageType.Close) { Console.WriteLine("Close loop complete"); break; } } } catch (Exception ex) { Console.WriteLine("Exception in receive - {0}", ex.Message); } } } }
问题是客户端在CloseAsync
调用时CloseAsync
。
在这种情况下,正确关闭WebSocket的正确方法是什么?
想出这个。
服务器
基本上,我不得不调用ClientWebSocket.CloseOutputAsync
(而不是CloseAsync
)方法告诉框架不再从客户端发送输出。
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
客户
然后在Receive
函数中,我不得不允许套接字状态WebSocketState.CloseSent
从服务器接收Close
响应
static async Task Receive(ClientWebSocket socket) { try { byte[] recvBuffer = new byte[64 * 1024]; while (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseSent) { var result = await socket.ReceiveAsync(new ArraySegment(recvBuffer), CancellationToken.None); Console.WriteLine("Client got {0} bytes", result.Count); Console.WriteLine(Encoding.UTF8.GetString(recvBuffer, 0, result.Count)); if (result.MessageType == WebSocketMessageType.Close) { Console.WriteLine("Close loop complete"); break; } } } catch (Exception ex) { Console.WriteLine("Exception in receive - {0}", ex.Message); } }
我建议你看看这些链接:
异步服务器: https : //msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx
异步客户端: https : //msdn.microsoft.com/en-us/library/bew39x2a(v = vs.110).aspx
最近我用这些链接实现了类似的东西作为例子。 方法“BeginReceive”(用于服务器)和“BeginConnect”(用于客户端)启动每个新线程。 所以不会有任何阻碍
- ASP Web Api – IoC – 解析HttpRequestMessage
- Webapi2 – 在一个任务完成后从控制器操作返回,但继续进一步的异步处理
- 通过HttpClient发布文件时,HttpRequest.Files为空
- 如何向我未存储在数据库中的Web API响应中添加属性?
- 使用asp.net webapi实现oData inlinecount
- 在c#中手动解码OAuth承载令牌
- REST服务身份validation
- 无法将当前JSON数组(例如)反序列化为类型’TenantManagementWebApi.Entities.Tenant
- DelegatingHandler不执行ASP.Net Web Api