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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 02:57:08