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

WinForm中TcpClient+NetworkStream并行发自定义数据报错排查

问题分析与解决方案

核心问题定位

  • 全局流对象未正确赋值:原代码声明了全局static NetworkStream stream,但_startServer方法内又创建了同名局部变量,导致全局流始终为null,Send按钮调用时要么空引用,要么访问已释放对象触发报错。
  • 连接生命周期管理混乱:客户端断开后局部流自动释放,但全局变量未更新;Stop按钮直接Dispose全局流,后续新连接的流也会受影响。
  • Send按钮逻辑阻塞UI:原while(true)死循环会卡死UI线程,且未做连接状态校验。
  • 跨线程UI访问不规范:虽用了Invoke,但可优化为更简洁的异步处理方式。

解决方案步骤

1. 重构连接对象存储

移除全局NetworkStream,改为存储TcpClient实例(流依赖于客户端对象,持有客户端更安全);用线程锁保护连接对象的访问,避免多线程竞态。

2. 异步化服务端逻辑

使用async/await实现异步IO操作,避免阻塞线程,同时支持并发处理客户端连接。

3. 修正Send按钮逻辑

移除死循环,先校验连接状态,再异步发送数据,避免阻塞UI;添加异常捕获,处理发送失败场景。

4. 完善服务启停逻辑

停止服务时先关闭监听,再释放所有活跃客户端连接,避免资源泄漏。

完整修正代码

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Server
{
    public partial class Form1 : Form
    {
        private TcpListener _tcpListener;
        private TcpClient _currentClient;
        private readonly object _clientLock = new object();

        public Form1()
        {
            InitializeComponent();
        }

        private string IsRealData(byte[] data)
        {
            return data.Length > 10 && data[10] == 100 ? "are you there?" : "NOTHING";
        }

        private async Task HandleClientAsync(TcpClient client)
        {
            try
            {
                using (var stream = client.GetStream())
                {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                    {
                        byte[] receivedData = new byte[bytesRead];
                        Buffer.BlockCopy(buffer, 0, receivedData, 0, bytesRead);
                        string receivedText = Encoding.ASCII.GetString(receivedData);

                        txt_log.Invoke((Action)(() =>
                        {
                            txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 收到: {receivedText}\r\n");
                        }));

                        if (IsRealData(receivedData) == "are you there?")
                        {
                            string replyText = "ok, here I am";
                            byte[] replyBytes = Encoding.ASCII.GetBytes(replyText);
                            await stream.WriteAsync(replyBytes, 0, replyBytes.Length);

                            txt_log.Invoke((Action)(() =>
                            {
                                txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 发送: {replyText}\r\n");
                            }));
                        }
                        else
                        {
                            string upperText = receivedText.ToUpper();
                            byte[] replyBytes = Encoding.ASCII.GetBytes(upperText);
                            await stream.WriteAsync(replyBytes, 0, replyBytes.Length);

                            txt_log.Invoke((Action)(() =>
                            {
                                txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 发送: {upperText}\r\n");
                            }));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                txt_log.Invoke((Action)(() =>
                {
                    txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 客户端断开: {ex.Message}\r\n");
                }));
            }
            finally
            {
                lock (_clientLock)
                {
                    if (_currentClient == client)
                    {
                        _currentClient.Close();
                        _currentClient = null;
                    }
                }
            }
        }

        private async Task StartServerAsync(string ip, int port)
        {
            try
            {
                _tcpListener = new TcpListener(IPAddress.Parse(ip), port);
                _tcpListener.Start();
                txt_log.Invoke((Action)(() =>
                {
                    txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 服务已启动,监听 {ip}:{port}\r\n");
                }));

                while (true)
                {
                    TcpClient newClient = await _tcpListener.AcceptTcpClientAsync();
                    txt_log.Invoke((Action)(() =>
                    {
                        txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 客户端连接: {newClient.Client.RemoteEndPoint}\r\n");
                    }));

                    lock (_clientLock)
                    {
                        _currentClient?.Close();
                        _currentClient = newClient;
                    }

                    _ = HandleClientAsync(newClient);
                }
            }
            catch (Exception ex)
            {
                txt_log.Invoke((Action)(() =>
                {
                    txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 服务停止: {ex.Message}\r\n");
                }));
            }
        }

        private async void bt_start_Click(object sender, EventArgs e)
        {
            if (_tcpListener != null && _tcpListener.Server.IsBound)
            {
                MessageBox.Show("服务已在运行");
                return;
            }
            txt_log.Clear();
            await StartServerAsync("192.168.10.15", 4444);
        }

        private void bt_stop_Click(object sender, EventArgs e)
        {
            _tcpListener?.Stop();
            lock (_clientLock)
            {
                _currentClient?.Close();
                _currentClient = null;
            }
            txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 服务已停止\r\n");
        }

        private async void bt_send_Click(object sender, EventArgs e)
        {
            string msg = txt_msg.Text.Trim();
            if (string.IsNullOrEmpty(msg))
            {
                MessageBox.Show("请输入要发送的内容");
                return;
            }

            TcpClient client;
            lock (_clientLock)
            {
                client = _currentClient;
            }

            if (client == null || !client.Connected)
            {
                MessageBox.Show("无活跃客户端连接");
                return;
            }

            try
            {
                byte[] msgBytes = Encoding.ASCII.GetBytes(msg);
                using (var stream = client.GetStream())
                {
                    await stream.WriteAsync(msgBytes, 0, msgBytes.Length);
                    txt_log.Invoke((Action)(() =>
                    {
                        txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 发送自定义数据: {msg}\r\n");
                    }));

                    byte[] replyBuffer = new byte[1024];
                    int replyBytes = await stream.ReadAsync(replyBuffer, 0, replyBuffer.Length);
                    if (replyBytes > 0)
                    {
                        string replyText = Encoding.ASCII.GetString(replyBuffer, 0, replyBytes);
                        txt_log.Invoke((Action)(() =>
                        {
                            txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 收到回复: {replyText}\r\n");
                        }));
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"发送失败: {ex.Message}");
                txt_log.Invoke((Action)(() =>
                {
                    txt_log.AppendText($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 发送错误: {ex.Message}\r\n");
                }));
            }
        }
    }
}

关键优化说明

  • 线程安全:用lock保护客户端对象的读写,避免多线程冲突。
  • 异步IO:全程使用异步方法,不阻塞主线程,提升服务响应性。
  • 资源自动释放:using语句自动清理流和客户端资源,防止内存泄漏。
  • 状态校验:发送前先检查连接状态,避免无效操作。
  • 异常处理:关键环节添加捕获,记录日志并提示用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:58:08