You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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异常通常由无限递归或事件无限循环触发导致,结合现象推测可能的原因:

  1. 重复绑定事件处理器:客户端启动初期连接状态波动,导致TryConnect被多次调用,每次都绑定新的事件处理器,同时重复发送订阅指令,引发大量事件并发触发,最终撑爆调用栈;
  2. LCU启动初期的事件风暴:客户端启动时会批量推送大量状态更新事件,若事件处理器同步处理这些事件,可能因调用栈过深引发溢出;
  3. 连接状态逻辑漏洞: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:36:04