C# WinForm应用TextBox中TCP网络流收发通信异常问题
问题背景
- 已完成客户端-服务端窗体通信程序基础搭建,可正常展示「等待客户端连接」「新客户端接入」类初始提示
- 故障表现:网络流写入、对端流数据读取逻辑异常,无法正常完成消息收发
代码问题清单
- 跨线程更新UI控件违规:WinForms控件仅允许在创建它的UI线程访问,直接在BackgroundWorker工作线程调用
AppendText修改文本框,会触发跨线程异常,直接中断流读写逻辑 - BackgroundWorker用法错误:
RunWorkerAsync接收的参数是传递给DoWork事件的上下文对象,直接传入同步执行的方法返回值,会导致handlingFunction、ConnectionToServer在UI线程运行,直接阻塞界面 - 服务端文本框事件逻辑错误:
serverTxtbox_TextChanged事件中直接调用Clear(),只要文本框内容变动就会被立刻清空,所有日志无法留存 - 流对象重复创建:客户端每次点击发送按钮都会新建
NetworkStream/StreamReader/StreamWriter,旧流未释放会占用连接,导致读写卡死 - 客户端发送逻辑死循环:
handlfun中写了while(true)无限循环,点击一次发送就会卡住UI线程,还会持续向服务端发送默认提示文本 - 空引用风险:服务端读流时如果客户端异常断开,
reader.ReadLine()会返回null,直接访问input.Length会触发空引用异常 - 字符串插值语法错误:C#字符串插值需要在引号前加
$前缀,原代码插值位置换行导致$作用域失效,${connections}的写法也不符合C#语法规范,正确写法为{connections} - 窗体初始化缺失:服务端
Form1构造函数未调用InitializeComponent(),窗体内所有控件不会正常加载,直接调用控件方法会抛出异常
修复后核心代码
服务端修复代码
首先补全窗体构造函数,新增跨线程安全更新文本的方法,修正后台线程、流读写、事件逻辑:
// 跨线程更新UI委托定义 delegate void SetTextCallback(string text); public Form1() { // 必须加控件初始化调用 InitializeComponent(); } // 跨线程安全追加文本方法 private void AppendTextSafe(string text) { if (serverTxtbox.InvokeRequired) { SetTextCallback d = new SetTextCallback(AppendTextSafe); this.Invoke(d, new object[] { text }); } else { serverTxtbox.AppendText(text + Environment.NewLine); } } private void CreateServer() { Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 9050); server.Bind(localEP); server.Listen(10); AppendTextSafe("waiting for a client"); BackgroundWorker worker1 = new BackgroundWorker(); // 正确绑定DoWork事件,让监听逻辑在后台线程运行 worker1.DoWork += (s, args) => handlingFunction(server); worker1.RunWorkerAsync(); } public void handlingFunction(Socket server) { while (true) { try { Socket client = server.Accept(); NetworkStream ns = new NetworkStream(client); StreamReader reader = new StreamReader(ns); // 开启AutoFlush,写入后自动刷新缓冲区,无需手动调用Flush StreamWriter writer = new StreamWriter(ns) { AutoFlush = true }; connections++; // 修正字符串插值写法 AppendTextSafe($"New client accepted: active connections {connections}"); writer.WriteLine("Welcome to my server"); string input; while (true) { input = reader.ReadLine(); // 新增null判断,处理客户端异常断开场景 if (input == null || input.Length == 0 || input.ToLower() == "exit") break; AppendTextSafe(input); writer.WriteLine(input); } ns.Close(); client.Close(); connections--; AppendTextSafe($"Client disconnected: {connections} active connections"); } catch (Exception ex) { connections--; AppendTextSafe($"Client disconnected: {connections} active connections, error: {ex.Message}"); } } } // 删除TextChanged事件里的Clear逻辑 private void serverTxtbox_TextChanged(object sender, EventArgs e) { // 不要在此处清空文本框 } private void button1_Click(object sender, EventArgs e) { try { CreateServer(); } catch (Exception) { AppendTextSafe("Connection failed .."); } }
客户端修复代码
修正流初始化逻辑,移除发送死循环,新增后台接收线程:
// 跨线程更新UI委托定义 delegate void SetTextCallback(string text); // 跨线程安全追加文本方法 private void AppendTextSafe(string text) { if (clientTextbox.InvokeRequired) { SetTextCallback d = new SetTextCallback(AppendTextSafe); this.Invoke(d, new object[] { text }); } else { clientTextbox.AppendText(text + Environment.NewLine); } } private void sendBtn_Click(object sender, EventArgs e) { if (writer == null || !client.Connected) { AppendTextSafe("Please connect to server first"); return; } // 读取输入框内容发送,发送后清空输入框即可,不要写无限循环 string input = clientTextbox.Text; writer.WriteLine(input); clientTextbox.Clear(); } private object ConnectionToServer(Socket client, IPEndPoint remoteEP) { try { client.Connect(remoteEP); // 连接成功后初始化一次流对象即可,不要每次发送都新建 stream = new NetworkStream(client); reader = new StreamReader(stream); writer = new StreamWriter(stream) { AutoFlush = true }; AppendTextSafe("Connected to server, enter message to send:"); // 后台常驻线程接收服务端消息 while (true) { string resp = reader.ReadLine(); if (resp == null) { AppendTextSafe("Server disconnected"); break; } AppendTextSafe($"Received: {resp}"); } } catch (SocketException ex) { AppendTextSafe($"Unable to connect to server: {ex.Message}"); } return client; } private void connectbtn_Click(object sender, EventArgs e) { client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050); BackgroundWorker worker1 = new BackgroundWorker(); worker1.DoWork += (s, args) => ConnectionToServer(client, remoteEP); worker1.RunWorkerAsync(); }
优化提示
- 给
StreamWriter设置AutoFlush = true后,不需要每次写入都手动调用Flush(),可以避免消息卡在缓冲区无法发出的问题 - 所有阻塞式网络操作(监听连接、读取流)都必须放在非UI线程运行,禁止在主线程执行长时间阻塞逻辑
- 不要在
TextChanged这类高频触发的控件事件中编写重置、清空类逻辑,极易导致界面显示异常 - 网络流读写时必须判断
ReadLine返回值是否为null,对端断开连接时方法会直接返回null,直接访问返回值的属性会触发异常
内容的提问来源于stack exchange,提问作者ektfaa
相关产品推荐
相关产品推荐

