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

如何在不重置TCP Client的情况下通过按钮实现客户端消息发送?

搞定Windows Forms聊天室发送按钮的正确姿势

嘿,我来帮你解决这个发送消息的问题!先看看你现有代码里的几个明显小问题:

  • 每次点击按钮都新建TcpClient,但压根没调用Connect连服务器,这肯定发不出去啊
  • 编码类拼写错了:ASCIIEncoding asii应该是ASCIIEncoding ascii(少了个c哦)
  • 没正确处理流的资源释放,容易造成内存泄漏
  • 如果是持续聊天的场景,重复创建连接效率很低,最好复用一个全局的连接实例

下面给你两种方案,你可以根据自己的场景选:

方案一:短连接(每次发送新建连接,适合简单场景)

修改后的发送按钮代码,直接能用:

private void sendButton_Click(object sender, EventArgs e)
{
    // 先检查输入框有没有内容,别发空消息
    if (string.IsNullOrWhiteSpace(sendBox.Text))
    {
        MessageBox.Show("先输入要发的消息呀!");
        return;
    }

    try
    {
        // 这里替换成你的服务器IP和端口哈
        string serverIp = "127.0.0.1";
        int serverPort = 8888;

        // using语句会自动释放资源,不用手动关啦
        using (TcpClient tcpClient = new TcpClient())
        {
            // 先连服务器
            tcpClient.Connect(serverIp, serverPort);
            
            using (NetworkStream stream = tcpClient.GetStream())
            {
                ASCIIEncoding ascii = new ASCIIEncoding();
                // 把输入框的文字转成字节数组
                byte[] messageBytes = ascii.GetBytes(sendBox.Text);
                
                // 先发送消息长度(可选,但服务器端能更准确接收,避免粘包)
                byte[] lengthBytes = BitConverter.GetBytes(messageBytes.Length);
                stream.Write(lengthBytes, 0, lengthBytes.Length);
                
                // 发送消息内容
                stream.Write(messageBytes, 0, messageBytes.Length);
                
                // 发完清空输入框,方便下次输入
                sendBox.Text = string.Empty;
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show($"发送失败啦:{ex.Message}");
    }
}

这个方案的好处:

  • 用using自动释放资源,不用操心内存泄漏
  • 加了空输入判断,用户体验更好
  • 可选的长度发送,解决服务器端可能的粘包问题
  • 发完自动清空输入框,很贴心

方案二:长连接(复用连接,适合持续聊天)

如果是要一直聊天,频繁发消息,长连接更高效,不会每次都重新握手。步骤如下:

第一步:在窗体类里定义全局的连接和流

private TcpClient _tcpClient;
private NetworkStream _networkStream;
private ASCIIEncoding _asciiEncoder = new ASCIIEncoding();

第二步:窗体加载时先连上服务器

private void ChatForm_Load(object sender, EventArgs e)
{
    try
    {
        string serverIp = "127.0.0.1";
        int serverPort = 8888;
        
        _tcpClient = new TcpClient();
        _tcpClient.Connect(serverIp, serverPort);
        _networkStream = _tcpClient.GetStream();
        
        // 顺便启动个后台线程收服务器消息,实现双向聊天
        Task.Run(() => ReceiveMessages());
    }
    catch (Exception ex)
    {
        MessageBox.Show($"连不上服务器:{ex.Message}");
    }
}

第三步:修改发送按钮的代码

private void sendButton_Click(object sender, EventArgs e)
{
    if (string.IsNullOrWhiteSpace(sendBox.Text))
    {
        MessageBox.Show("先输入消息哦!");
        return;
    }
    
    // 先检查连接是不是正常的
    if (_tcpClient == null || !_tcpClient.Connected)
    {
        MessageBox.Show("和服务器断连啦,重新连一下吧!");
        return;
    }

    try
    {
        byte[] messageBytes = _asciiEncoder.GetBytes(sendBox.Text);
        // 先发送消息长度
        byte[] lengthBytes = BitConverter.GetBytes(messageBytes.Length);
        _networkStream.Write(lengthBytes, 0, lengthBytes.Length);
        // 发送消息内容
        _networkStream.Write(messageBytes, 0, messageBytes.Length);
        
        // 清空输入框
        sendBox.Text = string.Empty;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"发送失败:{ex.Message}");
        // 连接异常就清理资源
        CleanupConnection();
    }
}

第四步:加个收消息的方法和资源清理方法

private void ReceiveMessages()
{
    byte[] lengthBuffer = new byte[4];
    while (_tcpClient != null && _tcpClient.Connected)
    {
        try
        {
            // 先读消息长度
            int bytesRead = _networkStream.Read(lengthBuffer, 0, lengthBuffer.Length);
            if (bytesRead == 0)
            {
                // 服务器断开了
                Invoke((Action)(() => MessageBox.Show("服务器跑啦!")));
                CleanupConnection();
                break;
            }
            
            int messageLength = BitConverter.ToInt32(lengthBuffer, 0);
            byte[] messageBuffer = new byte[messageLength];
            bytesRead = 0;
            
            // 确保把完整消息读出来
            while (bytesRead < messageLength)
            {
                int remaining = messageLength - bytesRead;
                int read = _networkStream.Read(messageBuffer, bytesRead, remaining);
                if (read == 0)
                {
                    CleanupConnection();
                    break;
                }
                bytesRead += read;
            }
            
            if (bytesRead == messageLength)
            {
                string receivedMessage = _asciiEncoder.GetString(messageBuffer);
                // 跨线程更新UI,因为收消息是后台线程,不能直接改UI
                Invoke((Action)(() =>
                {
                    chatHistoryBox.AppendText($"对方说:{receivedMessage}{Environment.NewLine}");
                }));
            }
        }
        catch (Exception ex)
        {
            Invoke((Action)(() => MessageBox.Show($"收消息失败:{ex.Message}")));
            CleanupConnection();
            break;
        }
    }
}

private void CleanupConnection()
{
    _networkStream?.Close();
    _tcpClient?.Close();
    _networkStream = null;
    _tcpClient = null;
}

这个方案的好处:

  • 一次连接重复用,效率高
  • 后台线程自动收消息,实现真正的双向聊天
  • 处理了连接断开的情况,自动清理资源
  • 用Invoke保证UI更新的线程安全,不会报错

最后提醒你哦:服务器端也要对应处理,比如如果用了发送长度的方式,服务器端也要先读长度再读内容,这样才能正确拿到完整的消息~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:24:30