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

C# UDP服务器新增套接字后触发Socket异常10060求助

解决C# UDP服务器添加新套接字后触发Socket异常10060的问题

问题背景

此前开发的C# UDP服务器与客户端可正常通信,添加新的套接字对后,服务器持续触发Socket异常10060(无法连接远程主机)。通过Wireshark确认客户端数据包已送达服务器,但服务器无法接收。客户端与服务器部署在不同主机,第三个Slave套接字暂未使用。

核心错误分析

  1. 频繁创建UdpClient实例:定时器每10ms就创建新的UdpClient绑定到端口,导致端口重复占用、套接字资源泄漏,最终引发连接异常。
  2. 错误使用Connect方法:UDP是无连接协议,服务器端无需调用Connect——该操作会限制套接字仅接收指定IP/端口的数据包,而客户端发送时使用随机源端口,与服务器硬编码的端口不匹配,导致无法接收。
  3. 阻塞调用堆积:Receive是同步阻塞方法,定时器10ms触发一次,会导致大量阻塞操作堆积,占用线程资源,无法正常处理新数据包。
  4. 代码笔误:Slave3的代码中错误引用Global.serverSocket2设置超时,而非Global.serverSocket3,导致第三个套接字配置错误。
  5. 资源未释放:创建的UdpClient未调用Dispose,导致套接字资源无法回收,系统资源耗尽。

修复方案与代码示例

修复后的服务器代码

public class Global
{
    // UDP Server Slave 1
    public const int port = 5060;
    public static UdpClient serverSocket = null;
    public static IPEndPoint clientEndpoint = new IPEndPoint(IPAddress.Any, port);
    public static bool UDPListening = false;
    public static string[] values;

    // UDP Server Slave 2
    public const int port2 = 5061;
    public static UdpClient serverSocket2 = null;
    public static IPEndPoint clientEndpoint2 = new IPEndPoint(IPAddress.Any, port2);
    public static bool UDPListening2 = false;
    public static string[] values2;

    // UDP Server Slave 3
    public const int port3 = 5062;
    public static UdpClient serverSocket3 = null;
    public static IPEndPoint clientEndpoint3 = new IPEndPoint(IPAddress.Any, port3);
    public static bool UDPListening3 = false;
    public static string[] values3;
}

private void HMI_Load(object sender, System.EventArgs e)
{
    // 初始化UDP套接字,仅执行一次
    InitializeUdpSockets();

    // 使用定时器定期更新UI,避免阻塞接收线程
    System.Windows.Forms.Timer timer2 = new System.Windows.Forms.Timer();
    timer2.Interval = 100; // 调整为合理间隔,避免过于频繁
    timer2.Start();
    timer2.Tick += timer2_Tick;
}

private void InitializeUdpSockets()
{
    try
    {
        // 初始化Slave1
        Global.serverSocket = new UdpClient(Global.port);
        Global.serverSocket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
        StartAsyncReceive(Global.serverSocket, Global.clientEndpoint, (data) => {
            string receivedMessage = Encoding.ASCII.GetString(data);
            Global.values = receivedMessage.Split('/');
        });

        // 初始化Slave2
        Global.serverSocket2 = new UdpClient(Global.port2);
        Global.serverSocket2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
        StartAsyncReceive(Global.serverSocket2, Global.clientEndpoint2, (data) => {
            string receivedMessage2 = Encoding.ASCII.GetString(data);
            Global.values2 = receivedMessage2.Split('/');
        });

        // 初始化Slave3(暂未使用,可注释或保留)
        Global.serverSocket3 = new UdpClient(Global.port3);
        Global.serverSocket3.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
        StartAsyncReceive(Global.serverSocket3, Global.clientEndpoint3, (data) => {
            string receivedMessage3 = Encoding.ASCII.GetString(data);
            Global.values3 = receivedMessage3.Split('/');
        });

        Global.UDPListening = true;
    }
    catch (SocketException ex)
    {
        richTextBox1.Text += $"初始化套接字失败: {ex.Message}\n\r";
    }
}

private void StartAsyncReceive(UdpClient client, IPEndPoint endpoint, Action<byte[]> onDataReceived)
{
    client.BeginReceive((ar) => {
        try
        {
            byte[] data = client.EndReceive(ar, ref endpoint);
            onDataReceived?.Invoke(data);
            // 接收完成后继续异步监听
            StartAsyncReceive(client, endpoint, onDataReceived);
        }
        catch (SocketException ex)
        {
            richTextBox1.Text += $"接收异常: {ex.Message}\n\r";
            // 异常后重新启动监听
            StartAsyncReceive(client, endpoint, onDataReceived);
        }
    }, null);
}

private void timer2_Tick(object sender, EventArgs e)
{
    if (!Global.UDPListening)
    {
        richTextBox1.Text += "服务器未监听\n\r";
        return;
    }

    // 在这里更新UI或处理接收到的数据,避免在接收线程中操作UI
    if (Global.values != null)
    {
        // 处理Slave1的数据,示例:
        // richTextBox1.Text += $"Slave1数据: {string.Join(",", Global.values)}\n\r";
        // Global.values = null; // 处理后清空,避免重复处理
    }

    if (Global.values2 != null)
    {
        // 处理Slave2的数据,示例:
        // richTextBox1.Text += $"Slave2数据: {string.Join(",", Global.values2)}\n\r";
        // Global.values2 = null;
    }
}

// 窗体关闭时释放资源
private void HMI_FormClosing(object sender, FormClosingEventArgs e)
{
    Global.serverSocket?.Close();
    Global.serverSocket2?.Close();
    Global.serverSocket3?.Close();
}

客户端代码优化建议

客户端每次循环创建新UdpClient会浪费资源,建议复用实例:

static void Main(string[] args)
{
    string matrizDatos = "HYP01/1.1/2.1/3.1/4.1/5.1/6.1/7.1/8.1/9.1/10.1/11.1/12.1/13.1/14.1/15.1/16.1/1/1";
    string matrizDatos2 = "HYP02/16.1/15.1/14.1/13.1/12.1/11.1/10.1/9.1/8.1/7.1/6.1/5.1/4.1/3.1/2.1/1.1/1/1";

    // 复用UdpClient实例
    using (UdpClient client = new UdpClient())
    using (UdpClient client2 = new UdpClient())
    {
        IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("172.20.80.34"), 5060);
        IPEndPoint ipep2 = new IPEndPoint(IPAddress.Parse("172.20.80.34"), 5061);

        client.Connect(ipep);
        client2.Connect(ipep2);

        while (true)
        {
            Console.WriteLine(matrizDatos);
            byte[] packet = Encoding.ASCII.GetBytes(matrizDatos);
            client.Send(packet, packet.Length);

            Console.WriteLine(matrizDatos2);
            byte[] packet2 = Encoding.ASCII.GetBytes(matrizDatos2);
            client2.Send(packet2, packet2.Length);

            Thread.Sleep(1000);
        }
    }
}

关键修复点说明

  1. 初始化时创建套接字:仅在窗体加载时创建一次UdpClient实例,避免端口重复占用和资源泄漏。
  2. 异步接收:使用BeginReceive/EndReceive实现异步监听,避免阻塞UI线程或定时器线程。
  3. 移除不必要的Connect:服务器端使用IPAddress.Any监听所有客户端的数据包,无需限制特定发送方。
  4. 资源释放:窗体关闭时关闭所有UdpClient,回收系统资源。
  5. 修复代码笔误:Slave3的超时设置引用正确的套接字实例。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:24:28