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

Unity Mono环境下C#原生ClientWebSocket出现SslStream已释放异常求解

问题排查与修复方案

这个异常由两类问题共同触发:一是你现有代码的异步用法存在缺陷,二是Unity Mono运行时的ClientWebSocket本身对WSS连接的SslStream生命周期管理存在已知Bug,以下是可直接落地的修复方案:


1. 代码异步用法错误修复

  • SendAsync并发调用导致底层流竞争:你当前的Send方法调用SendAsync后没有等待执行完成就直接返回,多个发送请求并发执行时,Mono底层的SslStream读写会出现竞争,直接触发流提前释放。需要用队列串行化所有发送请求,禁止并发调用SendAsync:
private readonly Queue<ArraySegment<byte>> _sendQueue = new Queue<ArraySegment<byte>>();
private bool _isSending;

public async Task SendAsync(string message)
{
    var data = new ArraySegment<byte>(Encoding.UTF8.GetBytes(message));
    lock (_sendQueue)
    {
        if (_isSending)
        {
            _sendQueue.Enqueue(data);
            return;
        }
        _isSending = true;
    }
    await SendInternal(data);
}

private async Task SendInternal(ArraySegment<byte> data)
{
    try
    {
        while (true)
        {
            if (m_Client.State != WebSocketState.Open) return;
            await m_Client.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None);
            Logger.Instance.Error("Send>" + Encoding.UTF8.GetString(data));
            lock (_sendQueue)
            {
                if (_sendQueue.Count == 0)
                {
                    _isSending = false;
                    break;
                }
                data = _sendQueue.Dequeue();
            }
        }
    }
    catch (Exception ex)
    {
        lock (_sendQueue)
        {
            _isSending = false;
            _sendQueue.Clear();
        }
        Logger.Instance.Error(ex);
    }
}
  • ConnectAsync同步阻塞导致上下文异常:你在ContinueWith里调用Task.Delay(3000).Wait()同步阻塞线程,在Mono的异步上下文里会导致底层资源被意外回收。将ConnectAsync改为异步方法,去掉同步阻塞逻辑:
public async Task ConnectAsync(string p_URL)
{
    try
    {
        await m_Client.ConnectAsync(new Uri(p_URL), CancellationToken.None);
        if (m_Client.State != WebSocketState.Open)
        {
            OnError?.Invoke(m_Client.State, m_Client.CloseStatusDescription, null);
            return;
        }
        Logger.Instance.Error($"ConnectAsync {m_Client.State}");
        _ = StartListenerTask(); // 持有监听Task引用避免GC回收
        // 若确实需要延迟,改为await Task.Delay(3000),禁止用Wait()
        OnOpen?.Invoke();
    }
    catch (Exception ex)
    {
        OnError?.Invoke(m_Client.State, m_Client.CloseStatusDescription, ex);
    }
}
  • async void方法导致异常逃逸:将StartListenerTask的返回值从void改为Task,避免异常无法被捕获直接导致上下文崩溃。

2. Mono SslStream生命周期Bug修复

Unity 2020及更早版本的Mono运行时,ClientWebSocket对WSS连接的SslStream管理存在原生Bug,会提前释放未断开的流,可选择以下两种方案修复:

  • 方案一:升级到Unity 2021.3+ LTS版本,该版本已官方修复了这个缺陷,修复上述代码用法问题后即可正常运行。
  • 方案二:若无法升级Unity版本,初始化ClientWebSocket时手动配置Ssl校验回调,避免SslStream被自动回收:
private void InitializeClient()
{
    m_Client?.Abort();
    m_Client = new ClientWebSocket();
    // 生产环境请不要直接返回true,需补充证书校验逻辑,此处仅做兼容性测试用
    m_Client.Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
}

3. 额外稳定性优化

所有异步操作统一传入同一个CancellationToken,关闭连接时先取消令牌,再调用CloseAsync,最后执行Abort,避免多线程竞争访问已释放的对象。

内容的提问来源于stack exchange,提问作者HardCPP

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 06:39:00