C# UDP服务器新增套接字后触发Socket异常10060求助
解决C# UDP服务器添加新套接字后触发Socket异常10060的问题
问题背景
此前开发的C# UDP服务器与客户端可正常通信,添加新的套接字对后,服务器持续触发Socket异常10060(无法连接远程主机)。通过Wireshark确认客户端数据包已送达服务器,但服务器无法接收。客户端与服务器部署在不同主机,第三个Slave套接字暂未使用。
核心错误分析
- 频繁创建UdpClient实例:定时器每10ms就创建新的UdpClient绑定到端口,导致端口重复占用、套接字资源泄漏,最终引发连接异常。
- 错误使用
Connect方法:UDP是无连接协议,服务器端无需调用Connect——该操作会限制套接字仅接收指定IP/端口的数据包,而客户端发送时使用随机源端口,与服务器硬编码的端口不匹配,导致无法接收。 - 阻塞调用堆积:
Receive是同步阻塞方法,定时器10ms触发一次,会导致大量阻塞操作堆积,占用线程资源,无法正常处理新数据包。 - 代码笔误:Slave3的代码中错误引用
Global.serverSocket2设置超时,而非Global.serverSocket3,导致第三个套接字配置错误。 - 资源未释放:创建的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); } } }
关键修复点说明
- 初始化时创建套接字:仅在窗体加载时创建一次UdpClient实例,避免端口重复占用和资源泄漏。
- 异步接收:使用
BeginReceive/EndReceive实现异步监听,避免阻塞UI线程或定时器线程。 - 移除不必要的
Connect:服务器端使用IPAddress.Any监听所有客户端的数据包,无需限制特定发送方。 - 资源释放:窗体关闭时关闭所有UdpClient,回收系统资源。
- 修复代码笔误:Slave3的超时设置引用正确的套接字实例。
内容的提问来源于stack exchange,提问作者nachettt
相关产品推荐
相关产品推荐

