C# UDP应用可发送消息但无法接收的问题排查
问题描述
我正在编写一个基于UDP协议的C#小型应用,需要同时实现消息的发送与接收功能。目前应用能正常发送消息,但无法接收消息,通过Wireshark确认服务器已经发出响应消息。
通信流程如下:客户端从随机端口(如55567)向服务器1711端口发送消息,服务器从1711端口回复到该随机端口。我尝试将监听客户端设置为与发送客户端相同的端口时出现异常,猜测应该监听该随机端口而非1711,但尝试基础示例代码后仍无法接收响应。
相关代码如下:
using System; using System.ComponentModel; using System.Net; using System.Net.Sockets; using System.Text; namespace DeviceLinkApp { class Program { private const int DLPort = 1711; private static UdpClient receivingUdpClient; static void Main(string[] args) { BackgroundWorker senderWorker = new BackgroundWorker(); senderWorker.DoWork += new DoWorkEventHandler(SenderThread); senderWorker.RunWorkerAsync(); // 创建用于读取 incoming 数据的 UdpClient receivingUdpClient = new UdpClient(DLPort); try { receivingUdpClient.BeginReceive(new AsyncCallback(recv), null); } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.ReadLine(); } // 回调函数 private static void recv(IAsyncResult res) { IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 1711); byte[] received = receivingUdpClient.EndReceive(res, ref RemoteIpEndPoint); // 处理逻辑 Console.WriteLine(Encoding.UTF8.GetString(received)); receivingUdpClient.BeginReceive(new AsyncCallback(recv), null); } private static void SenderThread(object sender, DoWorkEventArgs args ) { UdpClient udpClient = new UdpClient(); while (true) { System.Threading.Thread.Sleep(2000); Byte[] sendBytes1 = Encoding.ASCII.GetBytes("R/115"); Byte[] sendBytes2 = Encoding.ASCII.GetBytes("R/103"); Byte[] sendBytes3 = Encoding.ASCII.GetBytes("R/40"); try { udpClient.Send(sendBytes1, sendBytes1.Length, "192.168.1.164", DLPort); udpClient.Send(sendBytes2, sendBytes2.Length, "192.168.1.164", DLPort); udpClient.Send(sendBytes3, sendBytes3.Length, "192.168.1.164", DLPort); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } }
解决方案
问题核心
当前代码存在两个关键问题:
- 发送和接收使用两个独立的UdpClient:发送用的UdpClient未绑定固定端口,系统会分配随机端口;而接收用的UdpClient绑定了1711端口,服务器回复的是发送时的随机端口,因此接收端无法捕获响应。
- 接收回调中初始化的
RemoteIpEndPoint指定了端口1711,这会限制仅接收来自该端口的消息,但服务器的回复实际来自客户端的随机发送端口,导致无法正确接收。
修复方案
复用同一个UdpClient处理发送和接收:这样发送时使用的端口就是该UdpClient绑定的端口,接收端监听同一个端口即可捕获服务器的回复。具体修改如下:
- 全局仅保留一个UdpClient实例,初始化时绑定
IPEndPoint.Any(让系统自动分配端口,也可指定固定端口)。 - 发送线程直接使用该全局UdpClient发送消息。
- 接收回调中,将
RemoteIpEndPoint初始化为IPAddress.Any, 0,允许接收来自任意端口的消息。
修改后的代码
using System; using System.ComponentModel; using System.Net; using System.Net.Sockets; using System.Text; namespace DeviceLinkApp { class Program { private const int DLPort = 1711; // 全局复用同一个UdpClient private static UdpClient _udpClient; static void Main(string[] args) { // 初始化UdpClient,绑定任意IP和系统分配的端口(也可指定固定端口,如new IPEndPoint(IPAddress.Any, 5000)) _udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); // 启动发送线程 BackgroundWorker senderWorker = new BackgroundWorker(); senderWorker.DoWork += SenderThread; senderWorker.RunWorkerAsync(); // 启动异步接收 try { _udpClient.BeginReceive(RecvCallback, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.ReadLine(); } private static void RecvCallback(IAsyncResult res) { // 初始化端点为任意IP和端口,用于接收来自服务器的响应 IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0); byte[] receivedBytes = _udpClient.EndReceive(res, ref remoteEndpoint); // 处理接收到的消息 string receivedMsg = Encoding.UTF8.GetString(receivedBytes); Console.WriteLine($"收到来自 {remoteEndpoint} 的消息: {receivedMsg}"); // 继续监听下一条消息 _udpClient.BeginReceive(RecvCallback, null); } private static void SenderThread(object sender, DoWorkEventArgs args) { while (true) { System.Threading.Thread.Sleep(2000); Byte[] sendBytes1 = Encoding.ASCII.GetBytes("R/115"); Byte[] sendBytes2 = Encoding.ASCII.GetBytes("R/103"); Byte[] sendBytes3 = Encoding.ASCII.GetBytes("R/40"); try { // 使用全局UdpClient发送消息到服务器的1711端口 _udpClient.Send(sendBytes1, sendBytes1.Length, "192.168.1.164", DLPort); _udpClient.Send(sendBytes2, sendBytes2.Length, "192.168.1.164", DLPort); _udpClient.Send(sendBytes3, sendBytes3.Length, "192.168.1.164", DLPort); Console.WriteLine("消息发送完成"); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } } }
额外说明
- 如果需要客户端使用固定端口发送和接收,只需将UdpClient的初始化改为
new UdpClient(new IPEndPoint(IPAddress.Any, 自定义端口)),确保端口未被占用即可。 - UDP是无连接协议,服务器会根据收到的消息源IP和端口进行回复,因此必须保证发送和接收使用同一个端口(即同一个UdpClient实例)才能捕获回复。
内容的提问来源于stack exchange,提问作者Joonatan Renel
相关产品推荐
相关产品推荐

