【TRIO-Basic从入门到精通教程十】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: %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);
	}

}


//运行软件,首先选择网卡


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



结论:数据包准时准备收到,未发生丢包和数据错误


本次实验结束,谢谢大家

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值