所有的WebSocket数据发送到客户端之前ASP.NET紧密结合 [英] ASP.NET close connection before sending all websocket data to client

查看:137
本文介绍了所有的WebSocket数据发送到客户端之前ASP.NET紧密结合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个简单的asp.net的WebSocket处理器作为一个远程数据处理服务器和客户端之间的网关。
我在本地机器上测试(win8的,IIS EX $ P $干燥综合征8),一切运作良好。但是,在蔚蓝的网站,ASP.NET关闭所有的WebSocket数据发送到客户端之前连接。

以下是我的数据传输code:

 内部类WebSocketStreamTransfer {    公共WebSocketStreamTransfer(的CancellationToken disconnectionToken){
        DisconnectionToken = disconnectionToken;
    }    私人的CancellationToken DisconnectionToken {
        得到;
        组;
    }    公共异步任务AcceptWebSocketConnection(WebSocketContext上下文){
        如果(上下文== NULL)
            抛出新的ArgumentNullException(上下文);
        的WebSocket的WebSocket = context.WebSocket;
        如果(的WebSocket == NULL)
            抛出新SocksOverHttpException(空的WebSocket);
        使用(康涅狄格州IConnection = ConnectionManagerFactory.ConnectionManager.CreateConnection(Guid.NewGuid()的ToString())){
            尝试{
                DisconnectionToken.Register(conn.Close);
                TaskCompletionSource<布尔> TCS =新TaskCompletionSource<布尔>(NULL);
                等待Task.WhenAny(SendDataToRemoteServer(康涅狄格州的WebSocket,DisconnectionToken,TCS),SendDataToClient(康涅狄格州的WebSocket,DisconnectionToken,tcs.Task));
            }赶上(例外五){
                Logger.LogException(E);
            }
        }
    }    内部静态异步任务SendDataToRemoteServer(康涅狄格州IConnection,WebSocket的WebSocket的,的CancellationToken cancelToken,TaskCompletionSource<布尔> TCS){
        尝试{
            ArraySegment<位>缓冲区=新ArraySegment<位>(新字节[ApplicationConfiguration.GetDefaultBufferSize()]);
            而(IsConnected(康涅狄格州,cancelToken,WebSocket的)){
                WebSocketReceiveResult结果=等待websocket.ReceiveAsync(缓冲,cancelToken);
                如果(websocket.State == WebSocketState.Open){
                    如果(result.MessageType == WebSocketMessageType.Binary){
                        如果(result.Count大于0){
                            如果(IsConnected(康涅狄格州,cancelToken,WebSocket的)){
                                INT numRead =等待conn.SendData(buffer.Array,0,result.Count,cancelToken);
                                如果(numRead大于0){
                                    tcs.TrySetResult(真); //通知SendDataToClient可以继续
                                }其他{
                                    Logger.LogError(客户不是内置的远程连接发送足够的数据);
                                    返回;
                                }
                            }其他{
                                Logger.LogInformation(SendDataToRemoteServer:取消将数据发送到远程服务器由于连接关闭);
                            }
                        }其他
                            Logger.LogInformation(接收空二进制消息);
                    }否则如果(result.MessageType == WebSocketMessageType.Text){
                        Logger.LogError(收到意想不到的短信);
                        返回;
                    }其他{
                        Logger.LogInformation(接收关闭消息);
                        等待websocket.CloseAsync(WebSocketCloseStatus.NormalClosure,关闭连接,cancelToken);
                        返回;
                    }
                }其他{
                    Logger.LogInformation(SendDataToRemoteServer:WebSocket连接通过客户端关闭);
                    返回;
                }
            }
        }最后{
            tcs.TrySetResult(真);
        }
    }    内部静态异步任务SendDataToClient(康涅狄格州IConnection,WebSocket的WebSocket的,的CancellationToken cancelToken,任务connectedTask){
        等待connectedTask;
        而(IsConnected(康涅狄格州,cancelToken,WebSocket的)){
            字节[]数据=等待conn.ReceiveData(cancelToken);
            如果(data.Length&下; = 0){
                Logger.LogInformation(SendDataToClient:从远程服务器上的数据为空);
                返回;
            }
            如果(IsConnected(康涅狄格州,cancelToken,WebSocket的)){
                等待websocket.SendAsync(新ArraySegment<位>(数据),WebSocketMessageType.Binary,真实,cancelToken);
            }其他{
                Logger.LogInformation(SendDataToClient:取消发送数据到客户端由于连接关闭);
            }
        }
    }    内部静态布尔IsConnected(康涅狄格州IConnection,的CancellationToken cancelToken,WebSocket的WebSocket的){
        布尔socketConnected = websocket.State == WebSocketState.Open;
        返回socketConnected&放大器;&安培; conn.Connected&功放;&安培; !cancelToken.IsCancellationRequested;
    }
}

问题场景:


  1. SendDataToRemoteServer等待客户数据和客户端还没有数据发送尚未


  2. SendDataToClient接收来自远程服务器的空数据,意味着远程服务器开始关闭连接。因此,完成SendDataToClient


  3. AcceptWebSocketConnection完成,因为 Task.WhenAny(SendDataToRemoteServer(康涅狄格州的WebSocket,DisconnectionToken,TCS),SendDataToClient(康涅狄格州的WebSocket,DisconnectionToken,tcs.Task))


  4. 预计ASP.NET关闭TCP连接,但ASP.NET关闭连接立即(天蓝色)前发送的所有数据。



解决方案

一个WebSocket的消息可以在不同的帧进行分割。如果完成的消息,你不检查。在code发送的信息形成WS连接到其它应该是这样的:

  WebSocketReceiveResult结果= NULL;

{
    结果=等待source.ReceiveAsync(缓冲,CancellationToken.None);
    VAR sendBuffer =新ArraySegment<&字节GT;(buffer.Array,buffer.Offset,result.Count);    等待target.SendAsync(sendBuffer,result.MessageType,result.EndOfMessage,CancellationToken.None);
}
而(result.EndOfMessage!);

您必须检查 EndOfMessage 属性,而继续阅读,而没有完成的消息。

它可以在本地计算机,因为你在本地以同样的方式没有受到缓冲,还是因为你试图消息较小。

I wrote a simple asp.net websocket handler as a gateway between a remote data processing server and clients. I tested in my local machine (win8, IIS EXPRESS 8) and everything worked well. But in azure website, ASP.NET closes connection before sending all websocket data to client.

Following is my data transfer code:

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;
    }
}

Problem scenario:

  1. SendDataToRemoteServer waiting for client data and client has not data to send yet

  2. SendDataToClient receive empty data from remote server, means remote server start closing connection. So finish SendDataToClient

  3. AcceptWebSocketConnection finish because Task.WhenAny(SendDataToRemoteServer(conn, websocket, DisconnectionToken, tcs), SendDataToClient(conn, websocket, DisconnectionToken, tcs.Task))

  4. Expect ASP.NET send all data before close tcp connection but ASP.NET close connection immediately (azure).

解决方案

A WebSocket message can be split in different frames. You are not checking if the message is completed. The code for sending information form a WS connection to other should look like this:

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);

You have to check the EndOfMessage property, and continue reading while the message is not completed.

It works in your local computer because locally you are no affected by the buffering in the same way, or because the messages you were trying were smaller.

这篇关于所有的WebSocket数据发送到客户端之前ASP.NET紧密结合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆