【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);
}
}
//运行软件,首先选择网卡

根据实际情况,选择正在使用并连接了控制器的网卡。

结论:数据包准时准备收到,未发生丢包和数据错误
本次实验结束,谢谢大家
