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
相关产品推荐
相关产品推荐

