基于UDP的网口设备通讯(一)

本文分享了一个基于UDP协议的上位机与下位机通讯项目的实践经验,详细介绍了如何使用.Net的Sockets库进行数据发送和接收,包括IP和端口设置、数据校验及处理等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题记

最近做了一个小项目,基于UDP协议的上位机与下位机通讯项目。项目不算大,代码量(算上UI布局)也就700来行。但是感觉学到的东西不少。开一篇记录,记录,记录一下。

使用到的库

其实网口通讯方式的上位机制作简单,不像是USB通讯需要很麻烦的引用第三方库。UDP通讯只需要使用.Net自带的Sockets库就可以了。
我使用的是.Send和.Receive方式实现发送和接收数据

功能实现

 //设置本机IP及端口(用于信息接收)
IPEndPoint localIpep = new IPEndPoint(IPAddress.Parse("192.168.123.1"), 8080);
//设计本机IP及端口(用于命令下发)
IPEndPoint localIpep2 = new IPEndPoint(IPAddress.Parse("192.168.123.1"), 1100);
//定义发送到的IP地址和端口号
IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Parse("192.168.123.100"), 1080);

注:此处定义了两组IP地址和三个端口

至于为什么要在本机开两个不同的端口:

主要是我的设备我想要做成实时不间断的接收下位机返回的消息,而又要实现设备的启停。尝试过如果只用一个相同的端口,在启停命令发送完之后使用.Close()的方法使上位机放开端口。但是不知道是什么原因,可能放开的比较慢,有延时感,如果紧接着读取数据就会抛出IP重复定义的异常。所以干脆直接定义了两个端口。

开始发送接收指令:

实例化用以接收发送

//用以发送消息
private UdpClient udpcSend;
//用以接收消息
 private UdpClient udpcRecv;
 //发送指令
public void SendMessage()
        {
        	//此句是获取UI界面的参数的方法,感兴趣的大佬可以使用WPF的绑定方法
            GetBytes();
            //将数据按照协议方式打包
            byte[] sendMessage = Pack();
            //将数据发送(数据源,长度,地址)
            udpcSend.Send(sendMessage, sendMessage.Length, remoteIpep);
        }
private void ReceiveMessage()
        {
        	//侦听所有网络接口上活动的IP地址
            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);
            //接收数据,前面bytRecv可以是一个缓存区
            bytRecv = udpcRecv.Receive(ref remoteIpep);
        }

至此其实简单的UDP数据接收和发送的功能就已经完成了。但是发送和接收都要按照协议格式呀。发送还算是简单,底层的人告诉发送格式,UDP发送自动添加包头包尾校验位之类的。麻烦在接收。

接收数据校验:

听着名字高大上,但是我更喜欢通俗的解释:其实就是收到的数据包中有很多没用的东西(比如包头,包尾,校验,地址等等我不是专门做互联网的所以对这些也没有深入研究。感兴趣的大佬可自行去了解)。然后我的下位机一个包还是分成四份发的。总之:我的任务就是拿到包头和包尾以及之间的关键数据。

获取需要的数据:

说实话,这个算法写的是真的蠢,希望能有大佬指点一二:

//校验包头所包含的数据
byte[] headCheck = { ******* /*意思了一下校验没正经写*/ };
//校验包尾所包含的数据
byte[] tailCheck = { ******* /*意思了一下校验没正经写*/};

现在要做的就是识别包头开始接收数据,将下位机发来的数据拼接起来。直到收到包尾。

小弟的想法是,在外部定义一个容器,当收到包头时先将这一包加入缓存区,接收下一包时候开始查找包尾,没有包尾的话就和前面的缓存区拼接直到收到包尾结束。当完整的一包接收完毕后将缓存区清空以查找下一组数据的包头。

//烂算法
private void ReceiveMessage()
        {
            IPEndPoint remoteIpep = new IPEndPoint(IPAddress.Any, 0);

            //读数据并验证数据
            while (true)
            {
                try
                {
                    //返回远程机发回的信息
                    bytRecv = udpcRecv.Receive(ref remoteIpep);

                    //如果缓存区为空找头tempData是定义的缓存区
                    if (tempData.Count == 0)
                    {
                        //进行八次校验
                        for (int i = 0; i < headCheck.Length; i++)
                        {
                            if (headCheck[i] != bytRecv[i])
                            {
                                break;
                            }
                            //如果八次全部相同则为所需的包头
                            if (i == 7)
                            {
                                //将包头加入到数据区
                                tempData.AddRange(bytRecv);
                            }
                        }
                    }
                    else
                    {
                        //缓存区不空找尾
                        for (int i = 0; i < tailCheck.Length; i++)
                        {
                            //如果没有找到检验尾,将数据插入缓存区
                            if (tailCheck[i] != bytRecv[bytRecv.Length - 8 + i])
                            {
                                //将此包与前面的包拼接
                                tempData.AddRange(bytRecv);
                                break;
                            }
                            if (i == 7)
                            {
                                //如果找到了校验尾:将数据插入缓存区后开始数据处理
                                tempData.AddRange(bytRecv);
                                //中间有一些数据处理的内容删掉了
                                tempData.Clear();
                                break;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    break;
                }
            }
        }

后记

自知码龄比较短,写的程序比较蠢。不敢误人子弟,所以这只是记录篇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值