ASP.NET Core中的Websocket端点的奇怪行为。需要睡眠后接受插座
我目前正在开发另一个websocket处理自定义中间件,并得到一个奇怪的问题,我需要在套接字接受后睡觉,无论哪种方式,我不能在第一次成功的连接后再连接。 ..ASP.NET Core中的Websocket端点的奇怪行为。需要睡眠后接受插座
事实上,我收到一个Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Unexpected end of request content
例外
这里是如何工作的(我会发布后段)
- 中间件处理的连接,如果它检测到它是一个一个的WebSocket
- 中间件根据URI路径查找自定义处理程序/控制器(反射在CustomHandlerClass派生属性上,或多或少像mvc模式的工作方式)
- 中间件接受套接字(等待httpContext.WebSockets.AcceptWebSocketAsync() )
- 中间件创建一个处理程序实例,并调用正确的方法并将其作为参数传递给套接字和httpContext(有用的获取ServiceProvider;))。调用的方法是一个异步任务
- 的推导CustomHandlerClass听一些数据
- 接收数据时,执行它的任务重,并发送进展情况回
- 回到中间件其关闭套接字
异常消息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);
问候所有