我有一个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<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);
}
}
c#客户端使用ClientWebSocket类连接到服务器并发送请求。它创建一个任务,用于从服务器接收与请求发送并行运行的响应。完成发送请求后,它会在套接字上调用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<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);
}
}
}
}
问题是客户端阻止了CloseAsync
电话。
在这种情况下优雅关闭WebSocket的正确方法是什么?
答案 0 :(得分:10)
想出来了。
服务器强>
基本上,我必须调用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<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);
}
}
答案 1 :(得分:-1)
我建议你看看这些链接:
异步服务器: 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”(用于客户端)启动每个新线程。所以不会有任何阻止
的东西