我写了一个简单的asp.net websocket处理程序作为远程数据处理服务器和客户端之间的网关.
我在我的本地机器(win8,IIS EXPRESS 8)中测试过,一切运行良好.但是在azure网站上,ASP.NET在将所有websocket数据发送到客户端之前关闭了连接.
我在我的本地机器(win8,IIS EXPRESS 8)中测试过,一切运行良好.但是在azure网站上,ASP.NET在将所有websocket数据发送到客户端之前关闭了连接.
以下是我的数据传输代码:
internal class WebSocketStreamTransfer{ public WebSocketStreamTransfer(CancellationToken disconnectionToken){ DisconnectionToken = disconnectionToken; } private CancellationToken DisconnectionToken{ get; set; } public async Task AcceptWebSocketConnection(WebSocketContext context) { if (context == null) throw new ArgumentNullException("context"); WebSocket websocket = context.WebSocket; if (websocket == null) throw new SocksOverHttpException("Null websocket"); using(IConnection conn = ConnectionManagerFactory.ConnectionManager.CreateConnection(Guid.NewGuid().ToString())) { try { DisconnectionToken.Register(conn.Close); TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(null); await Task.WhenAny(SendDataToRemoteServer(conn,websocket,DisconnectionToken,tcs),SendDataToClient(conn,tcs.Task)); } catch(Exception e) { Logger.LogException(e); } } } internal static async Task SendDataToRemoteServer(IConnection conn,WebSocket websocket,CancellationToken cancelToken,TaskCompletionSource<bool> tcs) { try { ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[ApplicationConfiguration.GetDefaultBufferSize()]); while (IsConnected(conn,cancelToken,websocket)) { WebSocketReceiveResult result = await websocket.ReceiveAsync(buffer,cancelToken); if (websocket.State == WebSocketState.Open) { if (result.MessageType == WebSocketMessageType.Binary) { if (result.Count > 0) { if (IsConnected(conn,websocket)) { int numRead = await conn.SendData(buffer.Array,result.Count,cancelToken); if (numRead > 0) { tcs.TrySetResult(true); // Notify SendDataToClient can continue }else{ Logger.LogError("Client not send enough data for remote connection built"); return; } } else { Logger.LogInformation("SendDataToRemoteServer: Cancel send data to remote server due to connection closed"); } } else Logger.LogInformation("Receive empty binary message"); } else if (result.MessageType == WebSocketMessageType.Text) { Logger.LogError("Receive unexpected text message"); return; } else { Logger.LogInformation("Receive close message"); await websocket.CloseAsync(WebSocketCloseStatus.NormalClosure,"Close Connection",cancelToken); return; } } else { Logger.LogInformation("SendDataToRemoteServer: WebSocket connection closed by client"); return; } } }finally{ tcs.TrySetResult(true); } } internal static async Task SendDataToClient(IConnection conn,Task connectedTask) { await connectedTask; while (IsConnected(conn,websocket)) { byte[] data = await conn.ReceiveData(cancelToken); if (data.Length <= 0) { Logger.LogInformation("SendDataToClient: Get empty data from remote server"); return; } if (IsConnected(conn,websocket)) { await websocket.SendAsync(new ArraySegment<byte>(data),WebSocketMessageType.Binary,true,cancelToken); } else { Logger.LogInformation("SendDataToClient: Cancel send data to client due to connection closed"); } } } internal static bool IsConnected(IConnection conn,WebSocket websocket) { bool socketConnected = websocket.State == WebSocketState.Open; return socketConnected && conn.Connected && !cancelToken.IsCancellationRequested; } }
问题场景:
> SendDataToRemoteServer等待客户端数据,客户端尚未发送数据
> SendDataToClient从远程服务器接收空数据,表示远程服务器开始关闭连接.所以完成SendDataToClient
> AcceptWebSocketConnection完成因为Task.WhenAny(SendDataToRemoteServer(conn,tcs.Task))
>期望ASP.NET在关闭tcp连接之前发送所有数据,但ASP.NET立即关闭连接(azure).
解决方法
WebSocket消息可以分成不同的帧.您没有检查消息是否已完成.从与其他WS连接发送信息的代码应如下所示:
WebSocketReceiveResult result = null; do { result = await source.ReceiveAsync(buffer,CancellationToken.None); var sendBuffer = new ArraySegment<Byte>(buffer.Array,buffer.Offset,result.Count); await target.SendAsync(sendBuffer,result.MessageType,result.EndOfMessage,CancellationToken.None); } while (!result.EndOfMessage);
您必须检查EndOfMessage属性,并在消息未完成时继续读取.
它适用于您的本地计算机,因为本地您不会以相同的方式受到缓冲的影响,或者因为您尝试的消息较小.