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

MAUI中ClientWebSocket连接成功后被中止的问题求助

问题排查与修复方案

核心问题分析

  1. 构造函数调用async void方法的隐患:构造函数无法支持异步执行,调用async void类型的ConnectToServer会让异步操作脱离上下文管理,容易引发隐性的连接状态异常。
  2. 空异常捕获导致无法定位问题:所有catch块均为空实现,WebSocket操作中出现的任何异常(如服务器断开、网络波动、权限限制)都会直接导致连接中止,且无法得知具体错误原因。
  3. 不合理的Task.Run使用:ReceiveMessages本身是异步方法,无需用Task.Run额外包裹,多余的线程切换可能引发上下文冲突。
  4. 未正确清理旧连接:重新创建ClientWebSocket实例前未关闭旧连接,可能导致资源泄漏,干扰新连接的稳定性。

修复后的代码

private ClientWebSocket _clientWebSocket = new ClientWebSocket();
private readonly Uri _serverUri = new Uri("ws://10.0.2.2:6969/chat");
private CancellationTokenSource _cts = new();

// 改用页面生命周期事件初始化连接,支持异步执行
protected override async void OnAppearing()
{
    base.OnAppearing();
    await ConnectToServer();
}

// 页面销毁时清理WebSocket资源
protected override void OnDisappearing()
{
    base.OnDisappearing();
    _cts.Cancel();
    _clientWebSocket?.CloseAsync(WebSocketCloseStatus.NormalClosure, "Page closed", CancellationToken.None);
    _clientWebSocket?.Dispose();
}

private async Task ConnectToServer()
{
    try
    {
        // 先关闭并销毁旧连接(如果存在)
        if (_clientWebSocket.State != WebSocketState.Closed && _clientWebSocket.State != WebSocketState.Aborted)
        {
            await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Reconnecting", CancellationToken.None);
            _clientWebSocket.Dispose();
        }

        _clientWebSocket = new ClientWebSocket();
        await _clientWebSocket.ConnectAsync(_serverUri, _cts.Token);
        
        // 直接启动接收任务,无需额外线程包裹
        _ = ReceiveMessages();
    }
    catch (Exception ex)
    {
        // 打印异常信息,便于排查具体问题
        Console.WriteLine($"连接服务器失败: {ex.Message}\n{ex.StackTrace}");
    }
}

private async Task ReceiveMessages()
{
    var buffer = new byte[1024 * 4];
    try
    {
        while (!_cts.IsCancellationRequested && _clientWebSocket.State == WebSocketState.Open)
        {
            WebSocketReceiveResult result;
            var message = new StringBuilder();

            do
            {
                result = await _clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _cts.Token);

                if (result.MessageType == WebSocketMessageType.Text)
                {
                    message.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));
                }
                else if (result.MessageType == WebSocketMessageType.Close)
                {
                    // 处理服务器发送的关闭帧
                    await _clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server closed", _cts.Token);
                    break;
                }
            }
            while (!result.EndOfMessage);

            if (message.Length > 0)
            {
                MainThread.BeginInvokeOnMainThread(() =>
                {
                    chatMessages.Children.Add(new Label { Text = message.ToString() });
                });
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"接收消息异常: {ex.Message}\n{ex.StackTrace}");
        // 可选:异常后自动尝试重连
        // await ConnectToServer();
    }
}

private async void SendMessage_Clicked(object sender, EventArgs e)
{
    try
    {
        // 发送前检查连接状态,异常则尝试重连
        if (_clientWebSocket.State != WebSocketState.Open)
        {
            Console.WriteLine("连接未打开,尝试重连");
            await ConnectToServer();
            if (_clientWebSocket.State != WebSocketState.Open)
            {
                Console.WriteLine("重连失败,无法发送消息");
                return;
            }
        }

        var message = edInputMessage.Text?.Trim();
        if (string.IsNullOrEmpty(message)) return;

        var buffer = Encoding.UTF8.GetBytes(message);
        await _clientWebSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, _cts.Token);

        MainThread.BeginInvokeOnMainThread(() =>
        {
            chatMessages.Children.Add(new Label { Text = $"Me: {message}" });
            edInputMessage.Text = "";
        });
    }
    catch (Exception ex)
    {
        Console.WriteLine($"发送消息失败: {ex.Message}\n{ex.StackTrace}");
    }
}

关键修复点说明

  • 生命周期方法替代构造函数异步调用:用OnAppearing初始化连接,确保异步操作在页面显示时稳定执行,上下文更可控。
  • 添加CancellationToken管理资源:页面销毁时取消WebSocket操作,避免资源泄漏。
  • 补全异常日志:所有异常捕获块添加控制台输出,直接定位错误根源(如服务器地址错误、网络权限问题等)。
  • 移除多余线程包裹:直接调用ReceiveMessages,避免不必要的线程切换引发的上下文问题。
  • 处理服务器关闭帧:主动响应服务器的关闭请求,规范连接断开流程。
  • 发送前重连机制:如果连接已中止,自动尝试重连后再发送消息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:56:05