【发布时间】:2014-08-08 05:21:17
【问题描述】:
我编写了一个简单的 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, websocket, DisconnectionToken, 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, cancelToken, websocket)) {
int numRead = await conn.SendData(buffer.Array, 0, 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, WebSocket websocket, CancellationToken cancelToken, Task connectedTask) {
await connectedTask;
while (IsConnected(conn, cancelToken, websocket)) {
byte[] data = await conn.ReceiveData(cancelToken);
if (data.Length <= 0) {
Logger.LogInformation("SendDataToClient: Get empty data from remote server");
return;
}
if (IsConnected(conn, cancelToken, 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, CancellationToken cancelToken, WebSocket websocket) {
bool socketConnected = websocket.State == WebSocketState.Open;
return socketConnected && conn.Connected && !cancelToken.IsCancellationRequested;
}
}
问题场景:
SendDataToRemoteServer 正在等待客户端数据并且客户端还没有数据要发送
SendDataToClient 从远程服务器接收空数据,表示远程服务器开始关闭连接。所以完成 SendDataToClient
AcceptWebSocketConnection 完成,因为
Task.WhenAny(SendDataToRemoteServer(conn, websocket, DisconnectionToken, tcs), SendDataToClient(conn, websocket, DisconnectionToken, tcs.Task))期望 ASP.NET 在关闭 tcp 连接之前发送所有数据,但 ASP.NET 立即关闭连接(天蓝色)。
【问题讨论】:
-
我也遇到了同样的问题,你找到原因了吗?提前致谢。
-
我没有要展示的示例,但基本上这会中断循环并且客户端永远不会收到消息:while (IsConnected()) { ... await SendAsync(...).配置等待(假);休息;并且它只发生在 Azure 中。我必须在“休息”之前添加 Task.Delay(1000) 作为解决方法。
-
@Zygimantas 我从来没有解决过 Azure 网站环境中的问题。我使用github.com/Bobris/Nowin 在 docker 容器中使用 mono 运行,一切正常。
-
“期望 ASP.NET 在关闭 tcp 连接之前发送所有数据,但 ASP.NET 立即关闭连接(天蓝色)。”关闭前需要发送哪些数据?在您的描述中,所有数据都是在步骤 1-3 中发送的,而不是新数据,对吧?
-
@Vhao,所有数据意味着 ASP.NET 不仅确保所有数据发送到网络缓冲区,而且确保所有数据包都得到客户端的 ACK 响应。从应用程序日志中,所有数据都传递给了 SendDataToClient 方法,但是从 Wireshark 中,ASP.NET 在客户端收到所有数据之前关闭了 tcp 连接。