UDP端口接收数据包异常:重复获取旧数据的排查求助
问题
刚接触UDP技术,需要编写代码检测UDP端口是否在接收数据,以此更新UI展示系统接收状态。由于只能使用UDP而非TCP,要通过这种方式判断远端发送UDP数据包的程序是否处于运行状态。该功能用于飞行模拟器,读取系统内容显示飞机飞行计划,页面需定期更新。
现有代码未抛出错误,但控制台始终输出旧数据,实际UDP数据包已更新,代码却一直获取旧数据,仅重启应用后页面才更新,怀疑数据未正确清除。相关代码如下:
while (runTolissCapt_Connect) //Keep checking for UDP Incoming { IPAddress servIPAddress = IPAddress.Parse(TolissIP); var servEndPoint = new IPEndPoint(servIPAddress, TolissPort_Capt); if (_TolissCaptUdpClient == null) //If we haven't setup the UdpClient yet { lbl_Toliss_Conn.Invoke((MethodInvoker)(() => lbl_Toliss_Conn.ForeColor = Color.DarkRed)); try { _TolissCaptUdpClient = new UdpClient(TolissPort_Capt); mcdu.SetTolissUpdClient(_TolissCaptUdpClient); } catch (Exception) { } } Byte[] receiveData = _TolissCaptUdpClient.Receive(ref servEndPoint); //Receive data string returnData = Encoding.ASCII.GetString(receiveData); //Format data if (returnData != null) //If we got some data { Toliss_Alive = true; Console.WriteLine(returnData); //Write UDP message to console } else { Toliss_Alive = false; } Array.Clear(receiveData, 0, receiveData.Length); //Attempt to clear previous UDP message returnData = ""; if (Toliss_Alive) //Change UI to reflect UDP status lbl_Toliss_Conn.Invoke((MethodInvoker)(() => lbl_Toliss_Conn.ForeColor = Color.LimeGreen)); else lbl_Toliss_Conn.Invoke((MethodInvoker)(() => lbl_Toliss_Conn.ForeColor = Color.DarkRed)); new ManualResetEvent(false).WaitOne(500); }
解决思路
- 排查UdpClient实例与系统缓存问题:
循环内每次重建servEndPoint,但UdpClient.Receive会修改该端点为实际发送方地址。你的catch块空吞了所有异常,可能导致UdpClient实例创建失败却仍在执行后续逻辑,或者无效实例一直在读取系统缓存中的旧数据。建议在catch块中添加日志输出(比如Console.WriteLine("UdpClient创建失败:" + ex.Message)),排查实例化问题。另外,UDP依赖系统网络缓存,若程序重启前有未处理的数据包残留,启动后会优先读取旧数据,可在UdpClient创建后先调用一次Receive丢弃可能的缓存数据。 - 修正Toliss_Alive的判断逻辑:
returnData永远不会为null,Encoding.ASCII.GetString处理空字节数组也会返回空字符串,所以当前else分支永远不会触发。应改为判断!string.IsNullOrEmpty(returnData)。同时,当前逻辑只要收到一次数据就将Toliss_Alive设为true,后续即使断连也不会重置,不符合“检测远端运行状态”的需求。建议添加时间戳记录:每次收到数据时更新lastReceiveTime,循环中检查当前时间与lastReceiveTime的差值,超过设定阈值(比如2秒)就将Toliss_Alive设为false。 - 优化线程与UI更新逻辑:
每次循环新建ManualResetEvent属于冗余开销,可提前创建一个实例复用。另外,UI更新无需每次循环执行,可记录当前UI状态(比如currentConnColor),只有当目标颜色与当前状态不同时才调用Invoke,避免不必要的UI刷新。 - 调试定位问题根源:
在接收数据时,补充打印发送方的IP、端口和数据包长度,确认是否真的收到新数据包:
这样可以区分是发送端重复发送旧数据,还是代码读取了缓存中的旧数据包。Console.WriteLine($"来自{servEndPoint}的数据包(长度:{receiveData.Length}):{returnData}");
内容的提问来源于stack exchange,提问作者Horgy
相关产品推荐
相关产品推荐

