使用WebSocketSharp在.NET 6中连接LCU API时触发StackOverflow异常
英雄联盟LCU API WebSocket StackOverflow异常问题排查与解决思路
问题概况
开发一款对接英雄联盟LCU API的桌面应用,基于自行修改的PoniLCU库实现客户端交互,遇到以下问题:
- 程序已启动但LOL客户端未运行时,启动客户端后WebSocket连接建立不久就抛出StackOverflow异常;
- 移除发送WAMP订阅指令的代码
SocketConnection.Send("[5, \"OnJsonApiEvent\"]")后,异常不再出现; - 客户端提前运行时,WebSocket事件处理完全正常,能通过
Debug.WriteLine看到HandleMessage的输出; - 首次启动客户端时,会触发两次连接/断开事件(OnOpen和OnDisconnect各触发两次),移除弹窗后该现象依然存在。
核心代码片段
连接逻辑的关键实现:
private void TryConnect() { try { if (IsConnected) return; var status = GetLeagueStatus(); if (status == null) return; ProcessInfo = status; var byteArray = Encoding.ASCII.GetBytes("riot:" + status.Item2); Client!.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); SocketConnection = new WebSocket("wss://127.0.0.1:" + status.Item3 + "/", "wamp"); SocketConnection.SetCredentials("riot", status.Item2, true); SocketConnection.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12; SocketConnection.SslConfiguration.ServerCertificateValidationCallback = (a, b, c, d) => true; // 绑定事件处理器 SocketConnection.OnOpen += (sender, e) => OnWebSocketOpen(); SocketConnection.OnClose += (sender, e) => OnWebSocketClose(e.Code, e.Reason, e.WasClean); SocketConnection.OnMessage += (sender, e) => onWebSocketMessage(sender!, e); SocketConnection.Connect(); SocketConnection.Send("[5, \"OnJsonApiEvent\"]"); ProcessInfo = status; IsConnected = true; } catch (Exception e) { ProcessInfo = null; IsConnected = false; if (!IsConnected) throw new InvalidOperationException($"Exception occurred trying to connect to League of Legends: {e.ToString()}"); } }
已尝试的无效方案
- 在
HandleMessage中检查重复事件,无法解决问题; - 添加
Thread.Sleep降低请求频率,无效果; - 更换其他WebSocket库,未成功适配LCU;
- 尝试降级.NET版本,未解决异常。
问题分析
StackOverflow异常通常由无限递归或事件无限循环触发导致,结合现象推测可能的原因:
- 重复绑定事件处理器:客户端启动初期连接状态波动,导致
TryConnect被多次调用,每次都绑定新的事件处理器,同时重复发送订阅指令,引发大量事件并发触发,最终撑爆调用栈; - LCU启动初期的事件风暴:客户端启动时会批量推送大量状态更新事件,若事件处理器同步处理这些事件,可能因调用栈过深引发溢出;
- 连接状态逻辑漏洞:
IsConnected标记或SocketConnection对象未正确清理,导致旧连接的事件仍在触发,与新连接的事件形成冲突。
解决建议
1. 避免重复绑定事件与并发调用
给TryConnect加锁,并在创建新连接前清理旧连接的事件绑定:
private readonly object _connectLock = new object(); private void TryConnect() { lock (_connectLock) { if (IsConnected) return; // 清理旧连接的事件绑定与资源 if (SocketConnection != null) { SocketConnection.OnOpen -= OnWebSocketOpen; SocketConnection.OnClose -= OnWebSocketClose; SocketConnection.OnMessage -= onWebSocketMessage; SocketConnection.Dispose(); SocketConnection = null; } var status = GetLeagueStatus(); if (status == null) return; // 后续连接逻辑保持不变... SocketConnection = new WebSocket("wss://127.0.0.1:" + status.Item3 + "/", "wamp"); // 重新绑定事件... SocketConnection.Connect(); } }
2. 延迟发送订阅指令
客户端启动初期连接不稳定,在OnWebSocketOpen事件中延迟发送订阅指令,避免触发初期的事件风暴:
private void OnWebSocketOpen() { // 延迟1秒发送订阅指令,等待客户端状态稳定 Task.Delay(1000).ContinueWith(t => { if (IsConnected && SocketConnection != null && !t.IsFaulted) { SocketConnection.Send("[5, \"OnJsonApiEvent\"]"); } }, TaskScheduler.FromCurrentSynchronizationContext()); }
同时移除TryConnect中SocketConnection.Connect()之后的订阅指令发送代码。
3. 异步缓冲处理事件
使用队列缓冲事件,异步处理避免同步调用栈过深:
private readonly ConcurrentQueue<string> _eventQueue = new ConcurrentQueue<string>(); private bool _isProcessingQueue; private void onWebSocketMessage(object sender, MessageEventArgs e) { _eventQueue.Enqueue(e.Message); ProcessEventQueue(); } private async void ProcessEventQueue() { if (_isProcessingQueue) return; _isProcessingQueue = true; while (_eventQueue.TryDequeue(out var message)) { await HandleMessageAsync(message); // 将HandleMessage改为异步方法 } _isProcessingQueue = false; }
4. 完善连接关闭后的清理逻辑
在OnWebSocketClose中彻底清理连接资源与状态标记:
private void OnWebSocketClose(ushort code, string reason, bool wasClean) { lock (_connectLock) { IsConnected = false; if (SocketConnection != null) { SocketConnection.OnOpen -= OnWebSocketOpen; SocketConnection.OnClose -= OnWebSocketClose; SocketConnection.OnMessage -= onWebSocketMessage; SocketConnection.Dispose(); SocketConnection = null; } } }
内容的提问来源于stack exchange,提问作者Seiori
相关产品推荐
相关产品推荐

