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

C#多线程TCP客户端使用同一端口收发数据连接异常咨询

问题结论

客户端收发数据共用同一个本地端口的要求完全可以实现,你当前的认知有误,报错是代码逻辑错误导致的。

报错根本原因

你在发送线程和接收线程分别创建了独立的Socket实例,且两个实例都尝试绑定同一个本地端口、连接同一个远端服务器的同一个端口。不管是否开启端口复用,同一台设备上同一个TCP四元组(源IP、源端口、目的IP、目的端口)只能对应一个Socket连接,两个Socket实例争抢同一组资源必然会抛出占用异常。

解决方案

Socket本身是全双工通信组件,同一个Socket实例天生支持同时执行发送和接收操作,你只需要全局仅初始化一次Socket实例,分别给两个线程调用Send和Receive方法即可,具体修改逻辑如下:

  1. 全局共享唯一的Socket实例,将Socket初始化、绑定、连接操作合并为单次执行逻辑,不要分散在收发线程中重复执行。参考初始化代码:
// 建议将Socket实例放到GlobalVar中全局共享
public void InitCommSocket()
{
    while (!GlobalVar.is_conn) { }
    try
    {
        GlobalVar.CommSocket = new Socket(AddressFamily.InterNetwork, GlobalVar._isTcp ? SocketType.Stream : SocketType.Dgram, GlobalVar._isTcp ? ProtocolType.Tcp : ProtocolType.Udp);
        IPEndPoint serverEndPoint = new IPEndPoint(Mul_ip, Mul_port);
        
        if (GlobalVar._isTcp)
        {
            GlobalVar.CommSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
            GlobalVar.CommSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            GlobalVar.CommSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            GlobalVar.CommSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, true);
        }
        else
        {
            GlobalVar.CommSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(Mul_ip, IPAddress.Any));
        }
        // 仅绑定一次本地端口
        GlobalVar.CommSocket.Bind(new IPEndPoint(IPAddress.Any, Mul_port));
        // 仅连接一次服务器
        GlobalVar.CommSocket.Connect(serverEndPoint);
        GlobalVar.socketWasConfiged = true;
    }
    catch(Exception e)
    {
        MessageBox.Show("Socket初始化失败:" + e.Message, "错误");
    }
}
  1. 简化发送线程逻辑,仅处理队列消息发送:
public void Send_msg()
{
    // 等待Socket初始化完成
    while (!GlobalVar.socketWasConfiged) { }
    try
    {
        while (!_shouldStop)
        {
            while (!Send_msg_queue.IsEmpty)
            {
                Send_msg_queue.TryDequeue(out CM_msg send_msg);
                byte[] send_buff = Encoder(send_msg);
                try
                {
                    // 直接调用全局Socket的Send方法
                    GlobalVar.CommSocket.Send(send_buff, SocketFlags.None);
                    GlobalVar.Is_Wd_Rec = false;
                }
                catch (Exception e)
                {
                    MessageBox.Show("发送消息出错:" + e.Message, "错误");
                }
            }
        }
    }
    catch(Exception e)
    {
        MessageBox.Show("发送线程异常:" + e.Message, "错误");
    }
}
  1. 简化接收线程逻辑,仅处理数据接收:
public void Receive_msg()
{
    while (!GlobalVar.socketWasConfiged) { }
    try
    {
        byte[] rec_buff = new byte[size];
        while (!_shouldStop)
        {
            // 直接调用全局Socket的Receive方法
            size_rec = GlobalVar.CommSocket.Receive(rec_buff);
            GlobalVar.Counter_auto += 1;
            Receive_msg_queue.Enqueue(Decoder(rec_buff));
            GlobalVar.Is_Wd_Rec = true;
        }
    }
    catch (Exception e)
    {
        MessageBox.Show("接收消息出错:" + e.Message);
    }
}

注意事项

  • 收发操作本身是线程安全的,Send和Receive调用互不影响,不需要额外加锁。
  • 遇到连接断开等异常时,统一销毁旧Socket实例、重新执行初始化逻辑即可,不要在收发线程中单独处理重连,避免资源冲突。
  • 不需要要求服务器修改回写端口,当前客户需求完全可以实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 04:24:02