Windows Service中Secure Websocket Client执行Send操作超时问题求助
- 该问题最初出现在Windows Server 2016运行环境中,近期在Windows Server 2022上测试时,对应类库作为控制台应用可正常运行,但作为Windows Service运行仍然失败,已尝试赋予Windows Service管理员权限也无效。
- 基于.Net 4.8开发的类库使用Marfusios Websocket-client与wss协议的WebSocket服务端通信,类库以Windows Service或控制台应用形式运行时,客户端完成连接后首次发送请求就会超时;但在同一台服务器上,将该客户端放在IIS 10托管的.Net Web应用中运行,可以正常连接和通信。
- Windows Service/控制台模式运行时,类库可以正常启动WebSocket客户端并完成连接,但是在WebSocket客户端OnReconnection事件触发的向服务端Send操作时会卡住。通过Fiddler代理抓包可以看到连接建立后KeepAlive的Ping-Pong报文正常交互,但抓不到OnReconnection中触发的Send消息。
- 最终抛出的异常栈如下:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.
at System.Net.WebSockets.WebSocketBase.WebSocketOperation.d__19.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.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.WebSockets.WebSocketBase.d__45.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.HandleNonSuccessAndDebuggerNotification(Task task)
at Websocket.Client.WebsocketClient.d__70.MoveNext()
- 运行客户端的Windows Server已安装有效的Starfield SSL证书,已配置全局TLS1.2协议。
- WebSocket服务端已确认使用TLS1.2协议,相关配置已通过WireShark抓包验证。
- 已尝试过ValidateServerCertificate自定义验证、System.Net.ServicePointManager.SecurityProtocol显式指定TLS1.2等方案,均未解决问题。
- 修复异步调用死锁问题
控制台应用和Windows Service默认没有ASP.NET场景下的专用同步上下文,如果在OnReconnection事件回调中使用了.Result、Wait()等阻塞式方法等待Send异步操作完成,会触发线程死锁,导致Send请求无法实际下发到网络层。将所有Send相关的调用改为纯异步await写法,同时添加ConfigureAwait(false)跳过同步上下文捕获,可解决该类死锁问题。 - 授予服务运行账户证书私钥读取权限
IIS运行时的应用池账户默认拥有本地计算机级证书的私钥读取权限,但Windows Service常用的Local System、Network Service等账户可能没有对应SSL证书的私钥访问权限。可通过以下操作配置:打开本地计算机证书管理器(运行certlm.msc),找到对应的Starfield SSL证书,右键选择「所有任务」-「管理私钥」,添加服务运行所用的账户,授予「读取」权限即可。 - 调整Websocket客户端配置
显式配置Marfusios Websocket-client的发送参数,避免内部队列阻塞,参考配置代码如下:var client = new WebsocketClient(new Uri("wss://your-server-endpoint")) { IsTextMessageMaskingEnabled = true, SendBufferSize = 1024 * 1024, ReconnectTimeout = TimeSpan.FromSeconds(30), ErrorReconnectTimeout = TimeSpan.FromSeconds(10) }; // 异步处理重连后的发送逻辑,避免阻塞事件循环 client.ReconnectionHappened.Subscribe(async info => { await client.Send("first-request-payload").ConfigureAwait(false); }); - 调整TCP协议参数
部分Windows Server版本的TCP自动调优功能会对长连接WebSocket的小包发送产生异常延迟,可通过管理员权限执行命令netsh int tcp set global autotuninglevel=disabled临时关闭验证,确认问题解决后再根据实际业务场景调整对应参数。
内容的提问来源于stack exchange,提问作者gmerkel

