C# 如何在.Net中优雅地关闭双向WebSocket
我有一个服务器,它从客户机接收一个二进制数据流,每读取4MB,就用另一个文本数据流进行响应。服务器使用IIS 8和 服务器C# 如何在.Net中优雅地关闭双向WebSocket,c#,asp.net-web-api,websocket,iis-8,C#,Asp.net Web Api,Websocket,Iis 8,我有一个服务器,它从客户机接收一个二进制数据流,每读取4MB,就用另一个文本数据流进行响应。服务器使用IIS 8和 服务器 public class WebSocketController : ApiController { public HttpResponseMessage Get() { if (!HttpContext.Current.IsWebSocketRequest) { return new HttpResp
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<byte>(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<byte>(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<byte>(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);
}
}
问题是客户端在CloseAsync
调用时阻塞
在这种情况下,如何正确地关闭WebSocket?我建议您查看以下链接: 异步服务器: 异步客户端: 最近,我以这些链接为例实现了类似的东西。方法“BeginReceive”(用于服务器)和“BeginConnect”(用于客户端)会启动每个新线程。所以不会有任何东西阻止这一点 服务器
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<byte>(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<byte>(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<byte>(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);
}
}
基本上,我必须调用ClientWebSocket.CloseOutputAsync
(而不是CloseAsync
)方法来告诉框架不再从客户端发送输出
await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
客户端
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<byte>(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<byte>(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);
}
}
}
}
然后在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<byte>(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);
}
}
静态异步任务接收(ClientWebSocket套接字)
{
尝试
{
字节[]recvBuffer=新字节[64*1024];
while(socket.State==WebSocketState.Open | | socket.State==WebSocketState.CloseSent)
{
var result=await socket.ReceiveAsync(新的ArraySegment(recvBuffer),CancellationToken.None);
WriteLine(“客户端获得了{0}字节”,result.Count);
WriteLine(Encoding.UTF8.GetString(recvBuffer,0,result.Count));
if(result.MessageType==WebSocketMessageType.Close)
{
控制台写入线(“闭环完成”);
打破
}
}
}
捕获(例外情况除外)
{
WriteLine(“receive-{0}中的异常”,例如Message);
}
}
这是一个非常大的接收缓冲区。当连接多个客户端时,您需要大量内存。