ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C# UDP网口通讯实战:从收发示例到工业数据采集与调试优化

C# UDP网口通讯实战:从收发示例到工业数据采集与调试优化 简介这份资源面向C#初学者与需要实现网口通讯的开发者聚焦UDP无连接通信的编程实践。包内以C#源码为主体包含客户端与服务端两套工程共63个文件涵盖18个cs源文件、2个sln解决方案、2个csproj项目文件以及exe、config、resx、resources等编译与配置产物压缩包约117KB结构完整可直接打开运行。内容围绕UdpClient类的Send与Receive方法展开演示了创建实例、指定本地监听端口、构造IPEndPoint目标端点、发送与接收数据包、解析字节流并关闭连接的完整流程同时涉及广播地址使用、端口范围限制、数据丢失与重传处理等注意事项。已有316人学习适合用来理解UDP与TCP在连接方式、可靠性、头部开销上的差异并借助示例快速搭建局域网内两台设备的通讯测试环境为后续网口通讯调试与抓包验证提供可复用的代码基础。1. 从 C#UDP.zip 说起网口通讯到底在解决什么问题车间里一台工控机跑着 C# 上位机旁边是 PLC、扭矩枪、扫码器、称重仪数据要实时汇总到 MES。串口不够用、距离拉不远、速率上不去于是网口通讯成了默认选项。C#UDP.zip 这类压缩包本质就是一套用 C# 写的 UDP 收发示例一端发、一端收走网口不建连接不握手。它解决的是「设备到上位机之间怎么用最轻的方式把数据搬过去」。适合谁做 C# 上位机、设备集成、产线数据采集的工程师尤其是需要快速验证网口通不通、报文对不对的场景。UDP 和 TCP 的区别在这里很关键TCP 可靠但慢UDP 快但可能丢包选哪个取决于你的数据能不能容忍丢一帧。2. UDP 通讯的底层逻辑与 C# 选型理由2.1 为什么网口通讯优先考虑 UDPUDP 是无连接协议发送端只管把数据报扔出去不关心对方收没收到。这个特性在工业现场反而成了优势PLC 周期广播状态、传感器高频上报、扭矩枪每次拧紧发一条结果这些场景要的是低延迟和高吞吐而不是每条都必须确认。TCP 的三次握手、重传、拥塞控制在局域网内反而是负担。常见做法是设备侧用 UDP 广播或单播上位机开一个端口监听收到就解析。C# 的UdpClient和Socket都能做前者封装好、上手快后者灵活、能控制更多底层参数。2.2 C# 里 UDP 的两种写法UdpClient 与 SocketUdpClient适合快速原型几行代码就能收发。Socket适合需要绑定特定网卡、设置缓冲区、处理多播的场景。下面是最小可运行示例先跑通再谈优化。using System; using System.Net; using System.Net.Sockets; using System.Text; class UdpReceiver { static void Main() { // 绑定本机所有网卡的 8888 端口 UdpClient receiver new UdpClient(8888); // 设置接收超时避免无限阻塞 receiver.Client.ReceiveTimeout 5000; IPEndPoint remoteEP new IPEndPoint(IPAddress.Any, 0); Console.WriteLine(UDP 接收端已启动监听 8888 端口...); while (true) { try { byte[] data receiver.Receive(ref remoteEP); string msg Encoding.UTF8.GetString(data); Console.WriteLine($来自 {remoteEP} 的报文: {msg}); } catch (SocketException ex) { // 超时或网络异常记录后继续 Console.WriteLine($接收异常: {ex.Message}); } } } }逻辑说明UdpClient(8888)绑定端口Receive阻塞等待数据报remoteEP会带回发送方地址。参数说明ReceiveTimeout设 5000 毫秒避免程序卡死IPAddress.Any表示监听所有网卡如果机器有多网卡且只想听某一个改成对应 IP。发送端对称写一个Send即可注意目标 IP 和端口要匹配。2.3 网口通讯的地址与端口规划工业现场常见坑是 IP 冲突和端口占用。建议把设备网段和办公网段分开比如设备用 192.168.1.x上位机用 192.168.1.100端口从 8000 往后排。UDP 端口测试可以用netstat -anu看本机监听情况或者用网络调试助手发一条测试报文。如果收不到先查防火墙再查网线最后查代码里的绑定地址是不是写成了127.0.0.1。3. 用 C# 实现 UDP 收发的最小闭环3.1 发送端代码与参数配置发送端要指定目标 IP 和端口把业务数据转成字节数组发出去。下面是一个带重试的发送示例。using System; using System.Net; using System.Net.Sockets; using System.Text; class UdpSender { static void Main() { // 目标设备 IP 和端口 IPEndPoint target new IPEndPoint(IPAddress.Parse(192.168.1.50), 8888); UdpClient sender new UdpClient(); // 设置发送超时 sender.Client.SendTimeout 3000; string payload TORQUE,001,25.6Nm,OK; byte[] data Encoding.UTF8.GetBytes(payload); for (int i 0; i 3; i) // 最多重试 3 次 { try { sender.Send(data, data.Length, target); Console.WriteLine($第 {i 1} 次发送成功: {payload}); break; } catch (SocketException ex) { Console.WriteLine($发送失败: {ex.Message}重试...); } } sender.Close(); } }逻辑说明Send直接发数据报不建立连接。参数说明SendTimeout防止网络异常时卡住重试次数根据业务容忍度定一般 3 次够用。注意UdpClient默认不绑定本地端口系统会随机分配如果对方需要固定源端口要先Bind。3.2 接收端的数据解析与线程处理接收端如果只用一个线程解析复杂报文时会丢包。常见做法是接收线程只负责收收到后扔进队列另一个线程解析。C# 里可以用ConcurrentQueue加Task。using System; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; class UdpPipeline { static ConcurrentQueuestring queue new ConcurrentQueuestring(); static void Main() { UdpClient receiver new UdpClient(8888); // 接收线程 Task.Run(() { IPEndPoint remoteEP new IPEndPoint(IPAddress.Any, 0); while (true) { byte[] data receiver.Receive(ref remoteEP); queue.Enqueue(Encoding.UTF8.GetString(data)); } }); // 解析线程 Task.Run(() { while (true) { if (queue.TryDequeue(out string msg)) { // 这里做业务解析比如拆分字段、写数据库 Console.WriteLine($解析: {msg}); } else { Task.Delay(10).Wait(); // 队列空时稍等 } } }); Console.ReadLine(); } }逻辑说明接收和解析分离避免解析耗时导致接收缓冲区溢出。参数说明Task.Delay(10)是轮询间隔太小浪费 CPU太大增加延迟10 毫秒是常用值。如果数据量很大可以换成BlockingCollection或Channel。3.3 报文格式设计与字节序处理工业协议常用两种格式文本逗号分隔和二进制固定字节。文本好调试二进制省带宽。C# 里处理二进制要注意字节序x86 是小端网络字节序是大端。BitConverter默认小端跨平台通讯时要用IPAddress.HostToNetworkOrder转换。下面是一个二进制解析片段。byte[] raw new byte[] { 0x01, 0x00, 0x64, 0x00 }; // 假设是设备号扭矩值 int deviceId raw[0]; // 扭矩值两个字节小端转大端 short torqueRaw BitConverter.ToInt16(raw, 1); short torque IPAddress.NetworkToHostOrder(torqueRaw); Console.WriteLine($设备 {deviceId}扭矩 {torque / 10.0} Nm);逻辑说明先取设备号再把两个字节按网络字节序转成本机序。参数说明torque / 10.0是假设协议里扭矩放大了 10 倍具体看设备手册。如果设备发的是大端直接用BitConverter读出来就是错的必须转。4. 网口通讯调试与常见问题排查4.1 收不到数据从防火墙到绑定地址逐层查现象发送端显示成功接收端没反应。原因Windows 防火墙默认拦 UDP 入站或者代码绑定了127.0.0.1只监听本地回环。解决在防火墙里放行对应端口代码里改成IPAddress.Any。另外如果机器有多网卡UdpClient(8888)会绑所有网卡但有些系统只路由到默认网卡最好显式绑定设备所在网卡的 IP。4.2 数据乱码编码与字节序的坑现象收到一堆问号或乱码。原因发送端用 UTF-8接收端用 GBK或者二进制协议字节序搞反。解决统一编码文本协议建议 UTF-8二进制协议先确认设备手册里的字节序再用NetworkToHostOrder转换。血泪经验是调试时先用网络调试助手发已知十六进制确认解析逻辑对了再对接真实设备。4.3 丢包严重缓冲区与发送频率的平衡现象高频发送时接收端丢包。原因接收缓冲区太小或者解析线程处理不过来。解决调大Socket.ReceiveBufferSize比如设成 1MB发送端加限流别一股脑猛发。UDP 本身不保证可靠如果业务不能丢要么在应用层加序号和重传要么换 TCP。4.4 端口被占用程序退出没释放现象重启程序报「地址已在使用」。原因上次退出时UdpClient没Close或者系统 TIME_WAIT。解决用using包裹或者显式Close。UDP 没有 TIME_WAIT但Socket选项ReuseAddress可以允许重用端口设置sender.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)。4.5 跨网段通讯失败路由与网关检查现象同网段能通跨网段不通。原因设备没配网关或者上位机路由表不对。解决ping目标 IPtracert看路径确认网关和子网掩码。如果设备不支持跨网段就在同网段加一台转发机。5. 进阶让 UDP 通讯更稳的几个技巧5.1 应用层序号与超时重传UDP 不可靠但业务可以自己补。每条报文加一个递增序号接收端发现跳号就请求重发。下面是一个简单实现。// 发送端 int seq 0; byte[] BuildPacket(string payload, int seq) { byte[] body Encoding.UTF8.GetBytes(payload); byte[] packet new byte[body.Length 4]; BitConverter.GetBytes(seq).CopyTo(packet, 0); // 前 4 字节是序号 body.CopyTo(packet, 4); return packet; }逻辑说明序号放在报文头接收端解析后检查是否连续。参数说明序号用int占 4 字节循环使用。如果对可靠性要求高可以加 ACK 机制但那就接近 TCP 了要权衡。5.2 多播与广播的适用场景多播适合一对多比如一个上位机给多台设备发同步指令。C# 里用UdpClient.JoinMulticastGroup加入组。广播适合设备发现发送到255.255.255.255但很多交换机默认不转发广播跨网段要配。注意多播地址范围是 224.0.0.0 到 239.255.255.255别用错。5.3 性能测试用 iperf3 打 UDP 流调优前先测网络底子。iperf3 可以打 UDP 流命令是iperf3 -c 192.168.1.50 -u -b 100M -t 30看丢包率和抖动。如果网络本身丢包代码再优化也没用。测完再调ReceiveBufferSize和线程模型。5.4 日志与抓包Wireshark 看报文真相代码日志只能看到应用层Wireshark 能看到网卡上的原始报文。过滤udp.port 8888看发送端有没有发出去、接收端有没有收到、内容对不对。常见翻车是发送端绑了错误网卡报文根本没出机器Wireshark 一抓就现形。我一般会在项目里留一个「调试模式」把收发报文同时写文件和控制台上线前再关掉。这样出问题时不用改代码直接看日志。UDP 通讯不难难的是现场环境千奇百怪多抓包、多打日志比猜靠谱。希望帮到你。本文还有配套的精品资源点击获取
返回列表