【TRIO-Basic从入门到精通教程十】UDP通讯实现网络串口化测试与应用_udp和串口通信
亲爱的朋友们,我们又见面了!在工业控制场合中,有时候需要给第三方各种硬件或电脑快速实时发送数据,在不清楚是否已成功建立连接情况下稳定播报数据,大家第一时间是否想到使用串口。
在不建立串口网络环境下,我们还有一个很好的选择,那就是UDP通讯。
我们来看下UDP通讯百科描述:
//============================================================================
UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是 OSI(Open System Interconnection, 开放式系统互联) 参考模型中一种无连接的 传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。UDP在IP报文的协议号是17。 UDP协议全称是用户数据报协议 [1] ,在 网络中它与 TCP协议一样用于处理数据包,是一种无连接的协议。在 OSI模型中,在第四层—— 传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在 计算机之间传输数据的网络应用。包括 网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。 与所熟知的TCP( 传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI( 开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将 网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
//============================================================================
UDP在IT领域有些缺点,大数据分割发送不能保证顺序,但在我们工业控制领域,足够满足要求。
废话少说,我们先来上控制器发送代码。
CLOSE#20 WA(100) OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE WA(50) DIM send_timept,cal_timept,cal_allnum AS INTEGER DIM str_array AS STRING(20) str_array="XG" str_array=str_array+"000000" str_array=str_array+CHR(13)+CHR(10) PRINT #0,str_array cal_allnum=0 TICKS=0 cal_timept=TICKS send_timept=20 WHILE TRUE WAIT UNTIL cal_timept-TICKS >=send_timept cal_timept=cal_timept-send_timept cal_allnum=cal_allnum+1 str_array="XG" str_array=str_array+STR(cal_allnum,0,6) ' str_array=str_array+"000000" str_array=str_array+CHR(13)+CHR(10) ' PRINT #0,str_array PRINT #20,str_array WEND CLOSE#20
上面代码解析:
OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE
打开控制器20端口,数据包UDP广播至192.168.0.10 8080端口
WAIT UNTIL cal_timept-TICKS >=send_timept
用于阻塞计时,定时发送数据包,定时时间20ms发送一次
//==============================================================================================================================
第三方接收是我的电脑
为方便接收调试和测试,数据包是否成功,电脑安装一个装包软件。下载和安装过程,这里就不做详细的介绍
Wireshark-win32-2.0.5.exe
我们已经实时收到来自控制数据包,收包时间稳定不丢包
//==============================================================================================================================
讲到这里,我们的任务还没有结束,我们还需要上位机编程的方式进行收包,这样数据才能准确拿出来使用。
上位机编程环境:Microsoft Visual Studio 2010
上位机抓包软件:WinPcap_4_1_3.exe
上位机抓包库 :WpdPack_4_1_2.zip
软件下载安装这块飘过,我们进入重点:
代码编程
//================================================================= //测试标题:UDP测试 //测试时间:2017-11-10 //测试人 :桂志微 //公司 :上海摩习恩自动化工程有限公司 //================================================================= #include "pcap.h" //IP地址 typedef struct ip_address{ u_char byte1; u_char byte2; u_char byte3; u_char byte4; }ip_address; //4字节 //IPV4帧头结构 typedef struct ip_header{ u_char ver_ihl; // 版本信息4BIT 信息长度4BIT 1字节 u_char tos; // 服务类型 1字节 u_short tlen; // 总长度 2字节 u_short identification; // 数据包标识 2字节 u_short flags_fo; // 标记3BIT 帧偏移13BIT 2字节 u_char ttl; // 活动时间 1字节 u_char proto; // 协议类型 1字节 u_short crc; // 校验 2字节 ip_address saddr; // 源IP地址 4字节 ip_address daddr; // 目标地址 4字节 u_int op_pad; // 填充选项 4字节 }ip_header; //24字节 //UDP帧头 typedef struct udp_header{ u_short sport; // 源端口 2字节 u_short dport; // 目标端口 2字节 u_short len; // 数据帧长度 2字节 u_short crc; // 校验 2字节 }udp_header; //8字节 //响应数据包句柄 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); int main() { //网卡驱动 pcap_if_t *alldevs; pcap_if_t *d; int inum; int i=0; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; u_int netmask; char packet_filter[] = "ip and udp"; struct bpf_program fcode; //获取所有网卡驱动链表 if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %sn", errbuf); exit(1); } //打印所有网卡驱动链表 for(d=alldevs; d; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)n", d->description); else printf(" (No description available)n"); } if(i==0) { printf("nNo interfaces found! Make sure WinPcap is installed.n"); return -1; } //选择驱动网卡 printf("Enter the interface number (1-%d):",i); scanf_s("%d", &inum); if(inum < 1 || inum > i) { printf("nInterface number out of range.n"); /* Free the device list */ pcap_freealldevs(alldevs); return -1; } //定位选择网卡驱动 for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++); //打开网卡驱动 if ( (adhandle= pcap_open(d->name, // 驱动名称 65536, // 捕获数据包 // 存放数据包长度65536 PCAP_OPENFLAG_PROMISCUOUS, // 混合模式 1000, // 读取超时 NULL, // 远程身份验证 errbuf // 错误缓冲 ) ) == NULL) { fprintf(stderr,"nUnable to open the adapter. %s is not supported by WinPcapn"); //释放驱动链表 pcap_freealldevs(alldevs); return -1; } //链路层检查,只支持以太网链路层 if(pcap_datalink(adhandle) != DLT_EN10MB) { fprintf(stderr,"nThis program works only on Ethernet networks.n"); //释放驱动链表 pcap_freealldevs(alldevs); return -1; } if(d->addresses != NULL) //获取接口首地址掩码 netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr; else //如果接口没有地址,我们提供C类网络掩码 netmask=0xffffff; //编译过滤器 if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask)<0 ) { fprintf(stderr,"nUnable to compile the packet filter. Check th,.n"); //释放驱动链表 pcap_freealldevs(alldevs); return -1; } //设置过滤器 if (pcap_setfilter(adhandle, &fcode)description); //释放驱动链表 pcap_freealldevs(alldevs); //启动循环捕获 pcap_loop(adhandle, 0, packet_handler, NULL); return 0; } //响应数据包句柄-每个数据包进行回调 void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) { struct tm ltime; char timestr[16]; char Bmark[20]; ip_header *ih; udp_header *uh; u_int ip_len; u_short sport,dport; time_t local_tv_sec; int indexP=0; //将时间戳转换为可读格式 local_tv_sec = header->ts.tv_sec; localtime_s(<ime, &local_tv_sec); strftime( timestr, sizeof timestr, "%H:%M:%S", ts.tv_usec, header->len); //pkt_data数据帧 //定位以太网IP帧位置 ih = (ip_header *) (pkt_data +14); //定位UDP数据帧位置 ip_len = (ih->ver_ihl & 0xf) * 4; uh = (udp_header *) ((u_char*)ih + ip_len); //从网络字节顺序转换为主机字节顺序 sport = ntohs( uh->sport ); dport = ntohs( uh->dport ); //定位UDP数据帧 Bmark[0]=*((u_char*)uh + 8+0); Bmark[1]=*((u_char*)uh + 8+1); if(Bmark[0]=='X' && Bmark[1]=='G') { for(indexP=0;indexP%d.%d.%d.%d.%d:%sn", ih->saddr.byte1, ih->saddr.byte2, ih->saddr.byte3, ih->saddr.byte4, sport, ih->daddr.byte1, ih->daddr.byte2, ih->daddr.byte3, ih->daddr.byte4, dport, Bmark); } }
//运行软件,首先选择网卡
根据实际情况,选择正在使用并连接了控制器的网卡。
结论:数据包准时准备收到,未发生丢包和数据错误
本次实验结束,谢谢大家