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

如何避免Socket自动断开?C#长连接拆分遇空引用异常

解决TcpClient拆分后引发的NullReferenceException问题

我完全理解你想要把Socket连接和消息收发解耦、实现长连接的需求——这是构建可靠网络通信程序的合理步骤。不过你遇到的NullReferenceException其实是因为静态TcpClient变量可能没有成功初始化,而且你没有在调用sendRec前检查连接状态导致的。

问题根源分析

在你拆分后的代码里:

  1. Connect函数如果遇到异常(比如服务器不可达、端口错误),client变量会保持null状态,但函数没有任何返回值告知调用者连接失败。
  2. MainWindow构造函数里直接调用sendRec,完全没判断Connect是否成功,导致client.GetStream()时触发空引用异常。
  3. 另外,长连接场景下要注意:不要随意关闭NetworkStream,否则会直接断开TcpClient的连接。

修复方案

下面是一步步的改进代码,解决空引用问题同时优化长连接的可靠性:

1. 给Connect函数添加连接状态返回值

让调用者明确知道连接是否成功,避免后续操作踩空:

static TcpClient client;

static bool Connect(String server) {
    try {
        const int port = 13000;
        // 如果之前有连接,先释放资源
        client?.Close();
        client = new TcpClient(server, port);
        return true; // 连接成功
    } catch (ArgumentNullException e) {
        Console.WriteLine("ArgumentNullException: {0}", e);
    } catch (SocketException e) {
        Console.WriteLine("SocketException: {0}", e);
    }
    return false; // 连接失败
}

2. 强化sendRec的前置检查与异常处理

在收发消息前先验证client的有效性,同时处理连接断开的情况:

static string sendRec(string message) {
    // 先检查客户端是否已连接
    if (client == null || !client.Connected) {
        Console.WriteLine("Error: Client is not connected. Call Connect() first.");
        return null;
    }

    NetworkStream stream = null;
    try {
        stream = client.GetStream();
        // 验证流是否可读写
        if (!stream.CanWrite || !stream.CanRead) {
            Console.WriteLine("Error: Network stream is unavailable.");
            return null;
        }

        // 发送消息
        Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
        stream.Write(data, 0, data.Length);

        // 接收响应
        data = new Byte[256];
        int bytes = stream.Read(data, 0, data.Length);
        string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
        Console.WriteLine("Received: {0}", responseData);
        return responseData;
    } catch (IOException e) {
        Console.WriteLine("IO Exception (connection may be broken): {0}", e);
        // 连接断开后重置客户端
        client?.Close();
        client = null;
    } catch (Exception e) {
        Console.WriteLine("Unexpected error: {0}", e);
    }
    return null;
}

3. 在调用时先判断连接状态

修改MainWindow构造函数,确保只有连接成功才调用收发函数:

public MainWindow() {
    InitializeComponent();
    bool isConnected = Connect("127.0.0.1");
    if (isConnected) {
        string a = sendRec("test");
    } else {
        Console.WriteLine("Failed to establish connection to server.");
    }
}

进阶优化:封装成单例类(更易维护)

如果后续要扩展功能,把TcpClient封装成单例类会比全局静态变量更优雅,也更便于管理连接生命周期:

public class TcpConnectionManager {
    private static TcpConnectionManager _instance;
    private TcpClient _client;
    private const int _port = 13000;

    // 私有构造函数确保单例
    private TcpConnectionManager() {}

    public static TcpConnectionManager Instance {
        get {
            if (_instance == null) {
                _instance = new TcpConnectionManager();
            }
            return _instance;
        }
    }

    public bool Connect(string server) {
        try {
            _client?.Close();
            _client = new TcpClient(server, _port);
            return true;
        } catch (ArgumentNullException e) {
            Console.WriteLine("ArgumentNullException: {0}", e);
        } catch (SocketException e) {
            Console.WriteLine("SocketException: {0}", e);
        }
        return false;
    }

    public string SendMessage(string message) {
        if (_client == null || !_client.Connected) {
            Console.WriteLine("Error: Client is not connected.");
            return null;
        }

        NetworkStream stream = null;
        try {
            stream = _client.GetStream();
            if (!stream.CanWrite || !stream.CanRead) {
                Console.WriteLine("Error: Stream is unavailable.");
                return null;
            }

            byte[] data = Encoding.ASCII.GetBytes(message);
            stream.Write(data, 0, data.Length);

            data = new byte[256];
            int bytesRead = stream.Read(data, 0, data.Length);
            return Encoding.ASCII.GetString(data, 0, bytesRead);
        } catch (IOException e) {
            Console.WriteLine("Connection lost: {0}", e);
            _client?.Close();
            _client = null;
        } catch (Exception e) {
            Console.WriteLine("Error sending message: {0}", e);
        }
        return null;
    }

    public void Disconnect() {
        _client?.Close();
        _client = null;
    }
}

使用时只需调用单例:

public MainWindow() {
    InitializeComponent();
    bool connected = TcpConnectionManager.Instance.Connect("127.0.0.1");
    if (connected) {
        string response = TcpConnectionManager.Instance.SendMessage("test");
        Console.WriteLine("Server response: {0}", response);
    }
}

这样你的代码结构更清晰,也更容易排查后续可能出现的问题。

内容的提问来源于stack exchange,提问作者Matěj Nemec

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:36:08