尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

C# UDP通讯实战:从UdpClient收发到Wireshark抓包排错

发布时间:2026/9/16 17:40:50

资讯中心
01
ARTICLE

C# UDP通讯实战:从UdpClient收发到Wireshark抓包排错

C# UDP通讯实战:从UdpClient收发到Wireshark抓包排错
简介这套C# UDP通信示例资源面向需要快速掌握UDP协议与网口通讯测试的.NET开发者提供客户端与服务端双工程源码涵盖UdpClient发送、接收、IPEndPoint端点配置及局域网内数据收发等关键操作适合初学者对照学习或在项目中直接复用。压缩包共63个文件体积仅117KB主要包含18个C#源码文件、6个可直接运行的exe程序、配套config配置文件、resources/resx资源文件以及sln工程文件结构清晰便于直接打开调试与验证。已有314人学习下载。通过这份资源读者不仅能看到完整的UDP通信代码实现还能结合示例了解网口通讯中的常见细节如端口设置、IP地址指定、接收阻塞处理等并据此完成基础的通讯联通性测试为后续深入网络编程打下基础。1. 两个工程一套协议C#UDP.zip 到底给了什么拿到C#UDP.zip的第一眼多数人会以为是个单文件示例解压后看到的却是C_UDP_Client与C_UDP_Server两个独立工程各自带.sln、.v12.suo编译目标锁定在 Visual Studio 2013 那套工具链上。这不是随手的 demo而是一份最小可运行的「网口通讯」对照实验一端发、一端收端口和 IP 全部硬编码把 UDP 无连接、不可靠、头部只有 8 字节这几个特性直接摆在你面前。适合正在做 C# 上位机、需要和设备走 UDP 报文的新手也适合想快速验证「对端到底收没收到」的老手。它最大的价值不在代码量而在于给你一个可以马上改 IP、改端口、改报文的骨架省掉从System.Net.Sockets名字空间摸索起手的半小时。下面按选型、实现、抓包验证、坑位排错的顺序拆。2. UdpClient 与 Socket 的选型以及双工程结构拆解2.1 为什么这个包用 UdpClient 而不是裸 SocketC# 里做 UDP 有两条路Socket类和UdpClient封装类。这两者在功能上等价UdpClient内部持有的是一个Socket实例只不过把ReceiveFrom/SendTo的端点参数、缓冲区分配、关闭逻辑包了一层。对比项UdpClientSocketSocketType.Dgram构造复杂度一行指定端口需指定 AddressFamily、SocketType、ProtocolType收包写法Receive(ref IPEndPoint)自动带回发送方ReceiveFrom(buffer, ref EndPoint)需自行转型异步支持ReceiveAsync/SendAsyncReceiveFromAsync/SendToAsync广播开关EnableBroadcast trueSetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true)适用场景业务报文收发、快速原型需要精细控制 TTL、复用地址、原始套接字这份资源选了UdpClient取舍很清楚网口通讯场景下报文通常是定长结构体或短字符串不需要改动内核级套接字选项用封装类能让C_UDP_Client和C_UDP_Server的代码都压在几十行以内。如果你后面要做组播、要设SO_REUSEADDR让多个进程抢同一个端口那就得退回裸Socket。2.2 两个工程的文件构成与启动顺序解压后目录大致是这样C_UDP_Client/ ├── C_UDP_Client.sln ├── C_UDP_Client.v12.suo └── C_UDP_Client/ ├── Program.cs ├── Properties/AssemblyInfo.cs └── C_UDP_Client.csproj C_UDP_Server/ ├── C_UDP_Server.sln ├── C_UDP_Server.v12.suo └── C_UDP_Server/ ├── Program.cs └── Properties/AssemblyInfo.cs.v12.suo是 VS2013 的用户选项文件二进制格式记录断点、窗口布局和编译无关。真正要关心的是两个Program.cs。启动顺序必须是先 Server 后 Client服务端UdpClient(port)绑定端口后进入阻塞式Receive客户端才发得出去。反过来先跑客户端第一次Send不会报错——UDP 不握手内核直接把包丢出去没人监听就静默丢弃你会以为发成功了其实对端根本没起。2.3 编译环境从 VS2013 迁到新版的处理.v12.suo对应的工程格式是 VS2013用 VS2019 / VS2022 打开会提示升级工具集。常见做法是右键解决方案 → 重定解决方案目标或者在.csproj里把ToolsVersion和TargetFrameworkVersion换成v4.7.2/v4.8。如果只想命令行验证msbuild C_UDP_Server.sln /p:ConfigurationRelease也能编但前提是本机装了对应版本的 MSBuild。不想折腾工程文件的话直接把两个Program.cs复制到新建的控制台项目里依赖只有System.Net和System.Net.Sockets零第三方包。3. 收发包的代码实现从绑定端口到数据解析3.1 服务端绑定端口与阻塞接收循环服务端的核心就两步先构造UdpClient绑定本地端口再在一个while里反复Receive。Receive是同步阻塞调用没包进来就卡在那一行不动这对单线程示例够用。using System; using System.Net; using System.Net.Sockets; using System.Text; class Program { static void Main(string[] args) { // 绑定本地 8000 端口监听所有网卡 UdpClient server new UdpClient(8000); // 不限定位对端接收任意来源的数据报 IPEndPoint remoteEP new IPEndPoint(IPAddress.Any, 0); Console.WriteLine(Server listening on 8000 ...); while (true) { // 阻塞等待直到有数据报到达 byte[] buffer server.Receive(ref remoteEP); string msg Encoding.UTF8.GetString(buffer); Console.WriteLine(From {0}:{1} - {2}, remoteEP.Address, remoteEP.Port, msg); // 原路回一个应答方便客户端确认链路通 byte[] ack Encoding.UTF8.GetBytes(ACK msg); server.Send(ack, ack.Length, remoteEP); } } }new UdpClient(8000)这个构造重载等价于Bind到0.0.0.0:8000意味着监听本机所有网卡局域网内别的设备也能发进来。remoteEP初值给IPAddress.Any, 0只是占位Receive(ref remoteEP)执行后会被填成真实发送方的地址和端口。回发的Send用的是同一个UdpClient实例这样源端口保持 8000客户端看到的应答来源端口一致不容易被防火墙当成无关流量拦掉。3.2 客户端构造目标端点与发送客户端第一次Send时如果之前没调用过Connect系统会自动分配一个临时源端口。目标端点里 IP 和端口缺一不可。using System; using System.Net; using System.Net.Sockets; using System.Text; class Program { static void Main(string[] args) { UdpClient client new UdpClient(); // 不绑定固定端口由系统分配 client.Client.ReceiveTimeout 3000; // 3 秒应答超时 IPEndPoint target new IPEndPoint( IPAddress.Parse(192.168.1.100), 8000); // 服务端地址 for (int i 0; i 5; i) { string text HELLO- i; byte[] data Encoding.UTF8.GetBytes(text); client.Send(data, data.Length, target); // 无连接直接甩出去 Console.WriteLine(Sent: text); } try { IPEndPoint from new IPEndPoint(IPAddress.Any, 0); byte[] resp client.Receive(ref from); Console.WriteLine(Reply: Encoding.UTF8.GetString(resp)); } catch (SocketException ex) { Console.WriteLine(No reply within timeout: ex.SocketErrorCode); } finally { client.Close(); } } }这里有几个参数值得说明。ReceiveTimeout通过client.Client访问底层Socket设置单位毫秒不设的话Receive会无限期阻塞调试时很容易让人以为程序死了。循环里连发 5 个包UDP 不保证顺序也不保证全到收不到第 2 个包是正常现象不要写依赖序号的业务逻辑。客户端不主动Bind源端口每次运行都可能变如果服务端有基于源端口的会话表就必须显式new UdpClient(localPort)固定住。3.3 报文边界为什么 UDP 不会粘包但要按包处理TCP 有粘包问题UDP 没有——Receive一次返回一个完整数据报Datagram 边界由内核保留。但反过来如果你用大于 1472 字节以太网 MTU 1500 减去 IP 头 20 和 UDP 头 8的缓冲发数据IP 层会分片任一碎片丢失整个报文就废了。报文设计上常见做法是首部 4 字节放魔数、2 字节放长度、2 字节放序号后面才是负载。字段长度说明Magic4 字节固定值如 0x55AA55AA用于过滤杂包Length2 字节负载长度网络字节序Seq2 字节递增序号用于检测丢包Payload变长业务数据建议不超过 1400 字节解析时用BitConverter.ToUInt16前记得处理大小端Windows 是小端如果对端是嵌入式设备发的大端序要用IPAddress.HostToNetworkOrder转一遍否则长度字段会读成天文数字。4. 网口通讯测试抓包、打流与常见失败排查4.1 用 Wireshark 过滤 UDP 并算包间隔代码跑起来只是第一步真正确认「网口通讯」是否正常得看线上流量。Wireshark 里常用两个过滤表达式udp.port 8000 udp.srcport 8000 ip.dst 192.168.1.50第一条只看 8000 端口的收发第二条限定方向避免双向包混在一起。想看前两包的时间差在过滤栏敲udp然后菜单 Statistics → Conversations切到 UDP 标签页能看到每个会话的包数和时间跨度要单看某两包间隔选中第一包在Frame里记下Time since previous frame或者用tcp.time_delta的类似思路加一列自定义时间差。发包间隔稳定在设定的 100ms 附近说明应用层节拍正常如果间隔抖动到几百毫秒问题多半在发送端的Thread.Sleep精度或者 UI 线程阻塞这跟 C# 循环数据采集和 UI 刷新卡顿是同一类根因——主线程被Receive占住界面就卡死。4.2 用 iperf3 做 UDP 打流摸底代码还没写完、想先确认两台机器的网络通路和最大吞吐用 iperf3 最快。服务端先起iperf3 -s -u -p 5201客户端按固定码率打 UDP 流iperf3 -c 192.168.1.100 -u -p 5201 -b 50M -t 30 -l 1400-u指定 UDP-b 50M是目标带宽 50 Mbps-t 30跑 30 秒-l 1400指定每个数据报 1400 字节贴近实际业务报文大小。结果里关注 Jitter 和 Lost/Total 两列抖动大说明链路或对端处理有瓶颈丢包率高说明带宽定高了。这个数字可以当作你 C# 程序的性能上限参考代码发送速率超过它丢包就是网络的问题不是代码的问题。4.3 收不到包的六种常见原因按排查顺序列一下我一般会看的点。服务端没起或崩了。先确认进程在端口在netstat -ano | findstr 8000里有UDP行。防火墙拦了入站 UDP。Windows 默认对未监听过的 UDP 端口会弹提示点了「取消」就静默丢弃去高级安全防火墙给程序或端口加放行规则。IP 写错或不在同一网段。192.168.1.x和192.168.0.x之间没路由就是不通先ping通再说 UDP。端口被占用。UdpClient构造时如果端口被独占且没设ReuseAddress会抛SocketException错误码 10048。发到了广播地址但没开广播。255.255.255.255发送前必须client.EnableBroadcast true否则抛 10013 权限错误。网卡多张绑到了错的接口。笔记本同时有有线和无线时IPAddress.Any是通的但指定IPAddress.Parse(192.168.1.100)就只在那一张卡上收。提示排查时先把发送端和接收端放在同一台机器上用127.0.0.1跑通再换成真实 IP能排掉一半环境问题。5. 进阶技巧把同步阻塞改成异步并保住 UI 响应示例代码用的是同步Receive放进 WinForm 或 WPF 这类 C# 上位机里主线程一卡界面就是「未响应」。改法是换成ReceiveAsync配合CancellationToken做优雅退出private UdpClient _udp; private CancellationTokenSource _cts; private async Task ReceiveLoopAsync() { _udp new UdpClient(8000); while (!_cts.IsCancellationRequested) { try { // 异步等待不占用 UI 线程 UdpReceiveResult result await _udp.ReceiveAsync(); string msg Encoding.UTF8.GetString(result.Buffer); // 回到 UI 线程更新控件 this.Invoke(new Action(() { listBoxLog.Items.Add(${result.RemoteEndPoint}: {msg}); })); } catch (ObjectDisposedException) { break; // 关闭时正常抛出直接退出循环 } catch (SocketException ex) { // 记录错误码继续循环避免偶发 ICMP 端口不可达把循环打断 Console.WriteLine(ex.SocketErrorCode); } } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _cts.Cancel(); _udp?.Close(); // Close 会让 await ReceiveAsync 抛 ObjectDisposedException }关键在于Close与ReceiveAsync的配合异步等待没法被CancellationToken直接打断最可靠的做法就是关掉UdpClient让挂起的ReceiveAsync抛ObjectDisposedException在catch里跳出循环。另外Windows 上如果对端不在UDP 发包可能收到 ICMP 端口不可达反映到下一次ReceiveAsync上就是SocketException错误码 10054这种异常应该吞掉继续循环而不是让整个接收线程退出。UI 更新必须Invoke回主线程直接在后台线程改控件照样会抛跨线程异常。这套结构套进任何 C# 上位机采集程序都通用接收、解析、刷新三段分开卡顿自然就少了。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。