ASP.NET Core websocket服务器 - 尝试将数据发送到客户端

时间:2016-10-06 11:02:24

标签: c# websocket asp.net-core

我正在尝试创建一个ASP.NET Core Web服务器,它通过websockets与客户端通信。我发现我的服务器可以毫无问题地连接到客户端并从客户端接收数据,但无法将数据发送回客户端。

我正在使用我在Python中开发的websocket客户端测试服务器,这允许我逐步完成代码,而似乎发生的事情是,在我的服务器发送数据之后,Python端的websocket使用的streamreader遇到了EOF并关闭,导致websocket本身关闭,错误代码为1006.我知道客户端没有问题,因为它与基于.NET Framework(我正在迁移的)的另一个版本的服务器相关。

我正在使用Microsoft.AspNetCore.WebSockets.Server v0.1.0。以下是我的project.json

{
  "dependencies": {
    "<<Company>>.Communications.<<Product>>Usb": "0.4.7",
    "<<Company>>.<<Product>>Web.Core": {
      "target": "project"
    },
    "<<Company>>.<<Product>>WebComponentPackage": "0.4.4",
    "Common.Logging": "3.4.0-Beta2",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.AspNetCore.WebSockets.Server": "0.1.0",
    "Microsoft.Composition": "1.0.30",
    "Microsoft.Extensions.DependencyModel": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.NETCore.App": {
      "version": "1.0.1",
      "type": "platform"
    },
    "System.Runtime.Loader": "4.0.0",
    "Thrower": "3.0.4"
  },

  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "web.config"
    ]
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

Startup.cs中,我定义了我的websocket中间件:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    ...

    var websocketManager = app.ApplicationServices.GetService<WebSocketManager>();

    app.UseWebSockets();
    app.Use(websocketManager.Acceptor);
}

我的WebSocketManager类处理websocket请求:

public async Task Acceptor(HttpContext context, Func<Task> tasks)
{
    if (!context.WebSockets.IsWebSocketRequest)
    {
        return;
    }

    IWebSocketBehaviour wsBehaviour;
    var socket = await context.WebSockets.AcceptWebSocketAsync();
    Debug.Assert(socket.State == WebSocketState.Open);

    if (!this.TryGetWebSocketBehaviour(context, socket, out wsBehaviour))
    {
        socket.Dispose();
        return;
    }

    this.websocketBehaviours.Add(wsBehaviour);
    await wsBehaviour.Run();
    this.websocketBehaviours.Remove(wsBehaviour);
}

我的抽象WebSocketBehaviour类定义Run如下:

public async Task Run()
{
    try
    {
        var buffer = new ArraySegment<byte>(new byte[BufferSize]);

        this.OnOpen();
        while (this.socket.State == WebSocketState.Open)
        {
            WebSocketReceiveResult receivedMessage;

            try
            {
                receivedMessage = await this.socket.ReceiveAsync(buffer, Token);
            }
            catch (IOException)
            {
                await Task.Delay(10);
                continue;
            }

            if (receivedMessage.MessageType == WebSocketMessageType.Text)
            {
                var data = buffer.Array.TakeWhile(x => x != 0).ToArray();
                var request = Encoding.UTF8.GetString(data, 0, data.Length);
                this.OnMessage(request);
            }
        }

        if (this.socket.CloseStatus.HasValue && (this.socket.CloseStatus.Value != WebSocketCloseStatus.NormalClosure))
        {
            this.OnError(this.socket.CloseStatus.Value, this.socket.CloseStatusDescription);
        }

        await this.socket.CloseAsync(this.socket.CloseStatus ?? WebSocketCloseStatus.Empty,
            this.socket.CloseStatusDescription, Token);
        this.OnClose();
    }
    finally
    {
        this.socket.Dispose();
    }
}

最后Send的定义如下:

protected async Task Send(string response)
{
    Raise.InvalidOperationException.If(this.socket.State != WebSocketState.Open);
    var buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(response));
    await this.socket.SendAsync(buffer, WebSocketMessageType.Text, true, Token);
}

在一个场景中,我使用我的websocket客户端发送信息请求,该信息应该提示服务器的响应。我观察到以下一系列行为:

  1. 客户端与服务器建立websocket连接。
  2. 客户端发送{"op":"get", "path":"..."}形式的JSON请求。我在客户端看到以下日志:

    2016-10-06 11:45:01,805 [DEBUG   ] asyncio - Using selector: SelectSelector
    2016-10-06 11:45:01,806 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,807 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,807 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,807 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,808 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,808 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,808 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:01,812 [INFO    ] <<Product>>.comms - Establishing connection to <<Product>> Web Service @ 127.0.0.1:5000
    2016-10-06 11:45:01,815 [DEBUG   ] asyncio - connect <socket.socket fd=860, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6> to ('127.0.0.1', 5000)
    2016-10-06 11:45:01,818 [DEBUG   ] asyncio - poll took 0.000 ms: 1 events
    2016-10-06 11:45:01,825 [DEBUG   ] asyncio - <socket.socket fd=860, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('127.0.0.1', 60824), raddr=('127.0.0.1', 5000)> connected to 127.0.0.1:5000: (<_SelectorSocketTransport fd=860 read=polling write=<idle, bufsize=0>>, <websockets.client.WebSocketClientProtocol object at 0x037B5EF0>)
    2016-10-06 11:45:02,031 [DEBUG   ] asyncio - poll took 203.000 ms: 1 events
    2016-10-06 11:45:02,035 [DEBUG   ] <<Product>>.comms - Connected using websocket address: ws://127.0.0.1:5000/
    2016-10-06 11:45:02,035 [DEBUG   ] <<Product>>.component - Using read handler
    2016-10-06 11:45:02,037 [DEBUG   ] <<Product>>.comms - Sending request: {"op": "get", "path": "<<Product>>.relays[0].enabled"}
    2016-10-06 11:45:02,040 [DEBUG   ] Rx - CurrentThreadScheduler.schedule(state=None)
    2016-10-06 11:45:02,041 [DEBUG   ] <<Product>>.comms - Listening for responses
    2016-10-06 11:45:02,043 [DEBUG   ] websockets.protocol - client >> Frame(fin=True, opcode=1, data=b'{"op": "get", "path": "<<Product>>.relays[0].enabled"}')
    

    在服务器端,我看到:

    info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
          Request starting HTTP/1.1 GET http://127.0.0.1:5000/
    
  3. 服务器生成响应:{"timestamp":1475754679475,"status":"success","path":"$.relays[0].enabled","module":"<<product>>.relayController","data":[false]}

  4. 服务器将此响应编码为字节序列并执行await this.socket.SendAsync(buffer, WebSocketMessageType.Text, true, Token);

  5. 在客户端,我看到了这些额外的日志:

    2016-10-06 11:56:53,155 [INFO    ] asyncio - poll took 333764.000 ms: 1 events
    2016-10-06 11:56:53,156 [DEBUG   ] asyncio - <_SelectorSocketTransport fd=820 read=polling write=<idle, bufsize=0>> received EOF
    2016-10-06 11:56:53,157 [INFO    ] websockets.protocol - Failing the WebSocket connection: 1006
    

    在服务器端,我通常看不到其他日志,但偶尔当我尝试这个时,我得到以下异常日志:

    Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4077
     ECONNRESET connection reset by peer
    info: Microsoft.AspNetCore.Server.Kestrel[14]
          Connection id "0HKVDQUJUEKU0" communication error
    Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -4081
     ECANCELED operation canceled
    fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
          An unhandled exception has occurred while executing the request
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<EnsureDataAvaila
    bleOrReadAsync>d__38.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReadNextFrameAsy
    nc>d__37.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReceiveAsync>d__
    36.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketBehaviour.<Run>d_
    _4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketManager.<Acceptor
    >d__4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
          An unhandled exception has occurred while executing the request
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<EnsureDataAvaila
    bleOrReadAsync>d__38.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReadNextFrameAsy
    nc>d__37.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReceiveAsync>d__
    36.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketBehaviour.<Run>d_
    _4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketManager.<Acceptor
    >d__4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    warn: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
          The response has already started, the error page middleware will not be ex
    ecuted.
    warn: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[0]
          The response has already started, the error page middleware will not be ex
    ecuted.
    fail: Microsoft.AspNetCore.Server.Kestrel[13]
          Connection id "0HKVDQUJUEKU1": An unhandled exception was thrown by the ap
    plication.
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<EnsureDataAvaila
    bleOrReadAsync>d__38.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReadNextFrameAsy
    nc>d__37.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReceiveAsync>d__
    36.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketBehaviour.<Run>d_
    _4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketManager.<Acceptor
    >d__4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<
    Invoke>d__3.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessi
    ngAsync>d__2.MoveNext()
    fail: Microsoft.AspNetCore.Server.Kestrel[13]
          Connection id "0HKVDQUJUEKU0": An unhandled exception was thrown by the ap
    plication.
    System.Threading.Tasks.TaskCanceledException: A task was canceled.
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<EnsureDataAvaila
    bleOrReadAsync>d__38.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReadNextFrameAsy
    nc>d__37.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.WebSockets.Protocol.CommonWebSocket.<ReceiveAsync>d__
    36.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketBehaviour.<Run>d_
    _4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
       at <<Company>>.<<Product>>Web.ServiceAdapters.WebSockets.WebSocketManager.<Acceptor
    >d__4.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>
    d__6.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<
    Invoke>d__3.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
    ification(Task task)
       at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessi
    ngAsync>d__2.MoveNext()
    info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 2630.6342ms 101
    info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 11495.3447ms 101
    

0 个答案:

没有答案