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

