亲爱的朋友们,我们又见面了!在工业控制场合中,有时候需要给第三方各种硬件或电脑快速实时发送数据,在不清楚是否已成功建立连接情况下稳定播报数据,大家第一时间是否想到使用串口?在不建立串口网络环境下,我们还有一个很好的选择,那就是UDP通讯。我们来看下UDP通讯百科描述:
//============================================================================
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: %s\n", 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 WinPcap\n");
//释放驱动链表
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 the syntax.\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
//设置过滤器
if (pcap_setfilter(adhandle, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter.\n");
//释放驱动链表
pcap_freealldevs(alldevs);
return -1;
}
printf("\nlistening on %s...\n", d->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", <ime);
//打印时间戳和包的长度
printf("%s.%.6d len:%d ", timestr, header->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<8;indexP++)
{
Bmark[indexP]=*((u_char*)uh + 8+indexP);
}
Bmark[indexP]='\0';
//打印数据包消息
//打印ip地址和udp端口
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d:%s\n",
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);
}
}
//运行软件,首先选择网卡
根据实际情况,选择正在使用并连接了控制器的网卡。
结论:数据包准时准备收到,未发生丢包和数据错误
本次实验结束,谢谢大家