ASP.NET Core中的Websocket端点的奇怪行为。需要睡眠后接受插座

问题描述:

我目前正在开发另一个websocket处理自定义中间件,并得到一个奇怪的问题,我需要在套接字接受后睡觉,无论哪种方式,我不能在第一次成功的连接后再连接。 ..ASP.NET Core中的Websocket端点的奇怪行为。需要睡眠后接受插座

事实上,我收到一个Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content例外

这里是如何工作的(我会​​发布后段)

  1. 中间件处理的连接,如果它检测到它是一个一个的WebSocket
  2. 中间件根据URI路径查找自定义处理程序/控制器(反射在CustomHandlerClass派生属性上,或多或少像mvc模式的工作方式)
  3. 中间件接受套接字(等待httpContext.WebSockets.AcceptWebSocketAsync() )
  4. 中间件创建一个处理程序实例,并调用正确的方法并将其作为参数传递给套接字和httpContext(有用的获取ServiceProvider;))。调用的方法是一个异步任务
  5. 的推导CustomHandlerClass听一些数据
  6. 接收数据时,执行它的任务重,并发送进展情况回
  7. 回到中间件其关闭套接字

异常消息The remote party closed the WebSocket connection without completing the close handshake.

内部异常

{Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content. 
    at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.PipeCompletion.ThrowFailed() 
    at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.Pipe.GetResult(ReadResult& result) 
    at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.Pipe.Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.IReadableBufferAwaiter.GetResult() 
    at Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines.ReadableBufferAwaitable.GetResult() 
    at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.MessageBody.<ReadAsync>d__22.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 
    at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.FrameRequestStream.<ReadAsyncInternal>d__21.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() 
    at System.Net.WebSockets.ManagedWebSocket.<EnsureBufferContainsAsync>d__70.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Net.WebSockets.ManagedWebSocket.<ReceiveAsyncPrivate>d__61.MoveNext()} 

堆栈跟踪

at System.Net.WebSockets.ManagedWebSocket.<ReceiveAsyncPrivate>d__61.MoveNext() 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 
    at Andra.MiddleWares.WebSocket.WebSocketHandler.<ReceiveStringAsync>d__5.MoveNext() in C:\\Users\\BillGates\\WindowsContinuum\\MiddleWares\\WebSocket\\WebSocketHandler.cs:line 79 
--- End of stack trace from previous location where exception was thrown --- 
    at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() 
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 
    at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() 
    at EDIBorneo.Controllers.ValidatorController.<ValidateFileService>d__3.MoveNext() in C:\\Users\\BillGates\\WindowsContinuum\\Controllers\\ValidatorController.cs:line 54 

我试图等待

public class WebSocketMiddleWare 
    { 
    private readonly RequestDelegate _nextMiddleWare; 

    public WebSocketMiddleWare(RequestDelegate next) { 
     this._nextMiddleWare = next; 
    } 

     public async Task Invoke(HttpContext httpContext) { 

     if (httpContext.WebSockets.IsWebSocketRequest) { 
      // Handle websocket request 
      await this.ProcessWebSocketRequest(httpContext); 
     } else { 
      await this._nextMiddleWare.Invoke(httpContext); 
     } 
    } 

    private async Task ProcessWebSocketRequest(HttpContext httpContext) { 
     string basePath; 
     // Find handler derivated class which can handle the request 
     Type handlerType = this.getHandlerType(httpContext.Request.Path, out basePath); 

     if (null != handlerType) { 
      // Find the method in the found handler that will perform the service 
      PathString servicePath = httpContext.Request.Path.ToString().Substring(basePath.Length); 
      MethodInfo method = this.getHandlerMethod(handlerType, servicePath); 

      if (null != method) { 
       // Handler type and method found, the socket can now be accepted 
       System.Net.WebSockets.WebSocket socket = await httpContext.WebSockets.AcceptWebSocketAsync(); 
       //Thread.Sleep(1000); <== Needed to handle more than one connection.... :'(
       // Creation of an handler instance 
       WebSocketHandler handler = (WebSocketHandler)Activator.CreateInstance(handlerType, httpContext, socket); 

       // Invoking the right method (which is a async Task) 
       method.Invoke(handler, null); 

       // Job is done, we close the socket 
       await handler.CloseSocket(); 
      } 

     } 

    } 


    private Type getHandlerType(PathString path, out string attributePath) { 
     attributePath = ""; 
     List<Type> handlerTypeList = Reflection.GetInstances<WebSocketHandler>(); 
     foreach (Type handlerType in handlerTypeList) { 
      foreach (var classAttribute in handlerType.GetCustomAttributes(false)) { 
       if (classAttribute.GetType() == typeof(WebSocketPathAttribute)) { 
        WebSocketPathAttribute attr = (WebSocketPathAttribute)classAttribute; 
        if (path.StartsWithSegments(attr.Path)) { 
         attributePath = attr.Path.ToString(); 
         return handlerType; 
        } 
       } 
      } 
     } 
     return null; 
    } 

    private MethodInfo getHandlerMethod(Type handlerType, PathString path) { 
     Type objType = handlerType; 
     MethodInfo foundMethod=null; 
     foreach (MethodInfo method in objType.GetMethods()) { 
      WebSocketPathAttribute attr = method.GetCustomAttribute<WebSocketPathAttribute>(false); 
      if (attr.Path.Equals(path)) { 
       foundMethod = method; 
       break; 
      } 
     } 
     return foundMethod; 
    } 
    } 
    } 

客户端侧是纯JavaScript,并且没有具体的超时配置:

var ws = new WebSocket(myUri); 
ws.onopen = function (evt) { 
     $this._handler.onSocketOpen(evt); 
    }; 
ws.onclose = function (evt) { 
     $this._handler.onSocketClose(evt); 
    }; 
ws.onmessage = function (evt) { 
     $this._handler.onSocketMessage(evt); 
    }; 
ws.onerror = function (evt) { 
     $this._handler.onSocketError(evt); 
    }; 

上传的文件大小是60kB,测试是在本地完成的。 我不明白这个例外,因为成功连接和失败连接之间的通话没有区别。

问题似乎每当我试图在同一页面或刷新(CTRL + F5)页面

后,如果我要怀疑什么其他的WebSocket调用,这将是与方法调用不明确等待的线程。

进一步调试并没有告诉我什么特别的

找到有关此异常的一些SO职位,但它涉及超时,所以我不认为它适用于我的问题

睡眠修复工作得很好,但很丑陋,有点不可靠(我不应该这样做......)。所以我寻求在这个问题上有所帮助=)

将无法​​测试任何修复,直到周一,得到了慈善这个周末;)

问候

正如我怀疑,问题来了从我调用通过反射以非线程方式检索的方法的事实(也就是我没有等待它)。

此方法使用异步方法接收和发送数据,并且我的父方法调用将与线程上下文中断。

所以我只需要以适当的异步方式调用它。

感谢Scott在这个岗位How to call a generic async method using reflection,我不得不使用他提供了扩展和替换我method.Invoke(handler, null)通过await method.InvokeAsync(handler, null).ConfigureAwait(false);

问候所有