已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
可运行在单片机上的UDP通讯协议的实现2010年04月22日 星期四 19:46 本文可以说是在我之前写的单片机驱动DM9000网卡芯片(详细调试过程)基础上增加了一些协议代码来实现具体UDP通信传输。在这里我重新强调一下,上篇文章是在介绍如何调硬件,目的是为了让应用程序可以使用这个芯片。而具体的使用就是使用三个函数:初始化、数据包发送和数据包接收。数据包接收是否基于中断还需要用户根据需要自行设置。总之,我们可以通过上篇文章了解到,对硬件的调试可以得到这三个有用的函数。 不同网卡芯片的驱动可能略有不同。这里不一一例举,所以首先需要说明的是,本文所讲的内容主要是如何用C代码来实现协议,并利用数据包发送、接收函数来实现通讯,基本是与硬件无关的。除中断外,本文唯一与硬件相关的地方就是大端或小端格式,这也在之前的文章中有提到过,在本文涉及到的地方会再次说明。也就是说使用不同网卡芯片都可以应用本文所写的代码。其次,本文所写出的协议部分是已经过简化的,代码较少,不需要操作系统支持。但仅能实现数据的收发,而且没有验证可靠性(需要时可自行验证)。适合用在资源有限的单片机系统中,或者需要用网络代替RS232通信的情况,当然也可以在操作系统中使用。可根据情况来选择或增减。 顺便提一下资源要求:ram最好大于2KB,实在不行也得要1KB(需要一定技巧,传输的数据内容很少,不需要全部读出数据包的情况);flash或rom4KB以上,基本的单片机都能达到;可用IO怎么也得有12个,控制个一般的芯片也需要这么多的。 在进行正文之前,我再啰嗦几句,本文是讲协议的实现。这里的协议部分可以从TCP/IP协议 第一卷 协议这本书中看到最详细最权威的讲解,如果有兴趣研究协议的话可以参考这本书(网上可以找到电子版的)。下面进入主题。 1、UDP通讯的实现过程简述 涉及到协议部分,很多人会感觉摸不清头绪,不知如何下手。所以看一看上面说的那本书还是很有帮助的。当然看了以下部分,你也会对协议有些了解的。 (1)初始化网卡芯片和其他外设(在网卡驱动部分已经做好了,这里重新说了一遍); (2)arp通讯获得目标机地址信息; (3)udp通讯收发数据(利用IP协议作媒介)。 看到这会不会有些失望呢,可实际上udp通讯就是这么简单的。 UDP是User Datagram Protocol的简称,中文名是用户数据包协议,是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务(这句话是在网上抄的)。 说的再简单点,udp通讯与RS232一样,只管将数据发送出去而不管对方是否正确的接收到了。在一些简单应用中,我们似乎也不大关心数据是否被对方正确的接收到了,因为传输过程中数据包损坏的情况也不多,肯定能被正确的接收到。或者我们有其他的办法去验证。这和TCP协议不一样,TCP协议是可靠的链接,发送一次数据需要三次握手来反复确认数据被正确无误的接收到了,否则会重新发送一遍,实现起来比较繁琐。有点跑题,不过看到这里应该可以明白udp协议是一种很简单的网络通讯协议。 简单解释一下上面三个过程。初始化不用解释了。ARP通讯是整个网络传输的开始,而且只需要运行一次。在单片机驱动DM9000网卡芯片(详细调试过程)【下】最后的部分已经讲清楚了。主要是解释第三条,udp通讯收发数据。 OSI参考模型中arp协议属于链路层(最底层),ip协议比arp协议高一层属于网络层(这一层还包括icmp和igmp协议),在往上一层是运输层,包括tcp协议和udp协议。但是按我的理解,从数据包格式的角度看,我把arp协议与ip协议放在同一级别,我们接收到的数据包中的前几十个字节用来判断是arp协议还是ip协议,也就是说这两个协议是互补相容的(下面程序中会做个过滤,数据包只接收arp或ip协议,最后处理的数据包中只能是arp协议或者是ip协议)。这种互补相容的协议同样也适用于tcp协议和udp协议上。既然有互不相容的协议,那么也就有相容的协议了,这种相容的协议就是指一个协议必须依赖于另一种协议才能实现,udp协议就是这样。我们可以这样理解,ip协议就像一件外套,udp协议好比一件衬衫,而真正的数据可以看做是穿衣服的人。穿衣服的人先穿上衬衫再穿外套,这两个协议之间的关系就是这样:udp协议将数据包起来,ip协议又将udp协议连同其中的数据一起包起来。也就是说,实际的数据经过udp协议的包装,在经过ip协议的包装之后才能发送出去。虽然看起来有些繁琐,但实际计算机端就是这样识别数据的。所谓的包装就是在被包装数据前加上一小段首部数据,一般几十个字节左右。 2、ARP协议的实现 这部分内容在单片机驱动DM9000网卡芯片(详细调试过程)【下】的后半部分已经讲过,这里为了完整性再重复一次。 在写所有协议之前,有些全局变量需要事先设定一下,如ip地址、mac地址等信息。另外,统一规定一下我们的单片机系统为“基板”,计算机端为“上位机”,以下叫起来方便。再规定一下:char型是8位,short型是16位,long型是32位。OK!/*/unsigned char my_macaddr6 = 0x00, 0x0a, 0x00, 0x01, 0x02, 0x03 ;/基板上mac地址,这里随便写6个字节。unsigned char my_ipaddr4 = 192, 168, 1, 207 ;/基板上ip地址,根据网关写入合适值。unsigned char server_macaddr6 = 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ;/上位机mac地址,通过arp协议获取后改变。unsigned char server_ipaddr4 = 192, 168, 1, 122;/上位机ip地址,看自己的电脑,这个应该会,不会就问问旁边的人吧。unsigned char transmit_buffer2048 = 0 ;/发送数据包缓存区unsigned char receive_buffer2048 = 0 ;/接收数据包缓存区/*如果为了节省ram,这两个区可以共用一个(因为发送和接收数据可以分开进行,而且一般网卡芯片内部都会有自己独立的接收和发送缓存的),容量也可以改小,写成“unsigned char data_buffer1000 = 0 ;”我的ram比较多,所以用了两个区,而且占用很大空间。*/unsigned long send_packet_length = 0;/发送数据包长度unsigned long receive_packet_length = 0;/接收数据包长度/*如果上面的数据缓存区共用一个的话,这两个存储数据包长度的变量也可以共用一个,写成“packet_length”*/*/ 这部分是全局变量,需要在所有函数之前定义。 在写arp协议之前先定义两个结构体:/以太网地址首部,我们用的网络就是以太网struct eth_hdr unsigned char d_mac6; /目标mac地址unsigned char s_mac6; /源mac地址unsigned short type; /协议类型,判断arp协议还是ip协议;/ARP首部=以太网首部+arp协议首部struct arp_hdr struct eth_hdr ethhdr; /以太网首部结构体,如上unsigned short hwtype; /硬件地址,“1”表示mac地址unsigned short protocol; /协议地址,“0x0800”表示ip地址unsigned char hwlen; /硬件地址长度,mac地址为6unsigned char protolen; /协议地址,ip地址为4unsigned short opcode; /操作码“1”表示arp请求,“2”表示arp应答unsigned char smac6; /源mac地址unsigned char sipaddr4; /源ip地址unsigned char dmac6; /目标mac地址unsigned char dipaddr4; /目标ip地址;#define ETH_TRBUF (struct eth_hdr *)&transmit_buffer0)#define ETH_REBUF (struct eth_hdr *)&receive_buffer0)/*宏定义均是为了书写方便,以以太网首部的方式指向数据包缓存#define ARP_TRBUF (struct arp_hdr *)&transmit_buffer0)#define ARP_REBUF (struct arp_hdr *)&receive_buffer0) 接下来是arp协议的代码,具体含义在前一篇文章中有,这里对arp实现原理不做详细介绍了。另外,本文认为数据包发送和接收函数已经像前篇文章中那样写好了:void sendpacket(unsigned char* datas, unsigned long length);/发送数据包/要发送的数据包存放在“transmit_buffer”中。unsigned short receivepacket(unsigned char* datas);/接收数据包,返回协议类型(arp或ip)/接收到的数据包存放在“receive_buffer”中。可以与发送数据包公用。/arp请求void arp_request(void)memcpy(ARP_TRBUF-ethhdr.d_mac, server_macaddr, 6);memcpy(ARP_TRBUF-ethhdr.s_mac, my_macaddr, 6);/*用到上面的函数,在文件中要写上“#include ”*/ARP_TRBUF-ethhdr.type = 0x0806;/arp协议,此处按大端格式/*注意!这里涉及到大端格式和小端格式问题,小端格式需要写成0x0608,也就是16位的高低两个字节转换一下,这个很重要,一定要弄清楚自己处理器的编译器的存储格式。我记得51、AVR等单片机的编译器是小端格式,所以要高低字节交换。我这里和下面的程序中都按大端格式写*/ARP_TRBUF-hwtype = 1;/此处按大端格式ARP_TRBUF-protocol = 0x0800;/ip协议,此处按大端格式ARP_TRBUF-hwlen = 6;ARP_TRBUF-protolen = 4;ARP_TRBUF-opcode = 1;/arp请求,此处按大端格式。/*以下凡是16位宽的数据我都按大端格式处理不在重复啦*/memcpy(ARP_TRBUF-smac, my_macaddr, 6);memcpy(ARP_TRBUF-sipaddr, my_ipaddr, 4);memcpy(ARP_TRBUF-dipaddr, server_ipaddr, 4);send_packet_length = 42;/14字节以太网首部+28字节arp首部=42字节sendpacket(tr_data, send_packet_length);/arp应答void arp_reply(void)/memcpy(ARP_TRBUF-ethhdr.d_mac, server_macaddr, 6);/memcpy(ARP_TRBUF-ethhdr.s_mac, my_macaddr, 6);ARP_TRBUF-ethhdr.type = 0x0806;ARP_TRBUF-hwtype = 1;ARP_TRBUF-protocol = 0x0800;ARP_TRBUF-hwlen = 6;ARP_TRBUF-protolen = 4;ARP_TRBUF-opcode = 2;/memcpy(ARP_TRBUF-smac, my_macaddr, 6);memcpy(ARP_TRBUF-sipaddr, my_ipaddr, 4);memcpy(ARP_TRBUF-dipaddr, server_ipaddr, 4);send_packet_length = 42;/14+28=42sendpacket(transmit_buffer, send_packet_length);/arp处理unsigned long arp_process(unsigned long len)if(len opcode = 1) /判断是否是arp请求包,记得这里也是大端格式,小端格式就变成0x0100了 if(ARP_REBUF-dipaddr0 = my_ipaddr0 &/判断是否是基板的mac地址 ARP_REBUF-dipaddr1 = my_ipaddr1 & ARP_REBUF-dipaddr2 = my_ipaddr2 & ARP_REBUF-dipaddr3 = my_ipaddr3) memcpy(ARP_TRBUF-ethhdr.d_mac, ARP_REBUF-ethhdr.s_mac, 6); memcpy(ARP_TRBUF-ethhdr.s_mac, ARP_REBUF-ethhdr.d_mac, 6); memcpy(ARP_TRBUF-dmac, ARP_REBUF-ethhdr.s_mac, 6); memcpy(ARP_TRBUF-smac, ARP_REBUF-ethhdr.d_mac, 6); arp_reply();/发送arp应答 return ARP_REPLYTHEREQUEST;/*ARP_REPLYTHEREQUEST 这个宏也是自己随便定义的数,表示接收到的是arp请求数据包包*/ else return ARP_NOTMYIP;/ARP_NOTMYIP 自定义宏,表示接收到的数据包不是基板的,放弃掉。 else if(ARP_REBUF-opcode = 2) /判断是arp应答包 if(ARP_REBUF-dipaddr0 = my_ipaddr0 & ARP_REBUF-dipaddr1 = my_ipaddr1 & ARP_REBUF-dipaddr2 = my_ipaddr2 & ARP_REBUF-dipaddr3 = my_ipaddr3) memcpy(server_macaddr, ARP_REBUF-smac, 6); return ARP_SAVETHEREPLY;/自定义宏,表示接收到arp应答,并且将上位机mac地址存储好了 else return ARP_NOTMYIP; else return ARP_UNKNOWN;/自定义宏,表示数据包出错 arp协议到这里就完成了。包含三个函数:void arp_request(void);void arp_reply(void);unsigned long arp_process(unsigned long len); arp协议具体原理可参考单片机驱动DM9000网卡芯片(详细调试过程)【下】,也可参考前面介绍的书。 3、IP协议的实现 ip协议用途很重要,但对于本文仅使用udp协议来说,它只是用于装载udp协议的。 首先,定义ip协议的首部结构体和一些必要的全局变量。/ip首部=以太网首部+ip协议首部struct ip_hdr struct eth_hdr ethhdr;/以太网首部unsigned char vhl, /ip协议的版本和首部长度。固定写法0x45,表示IP4,首部长5*32bit(20字节) tos;/TOS(0)(可不关心)unsigned short len,/整个ip数据包长度,包括ip首部 ipid,/ip标识,也可以看做是ip计数器 ipoffset;/掩码和偏移(可不关心)unsigned char ttl,/ip包的生命周期(写成固定值,可不关心) proto;/ip包装载的协议类型, 1表示ICMP, 2表示IGMP, 6表示TCP, 17表示UDPunsigned short ipchksum; /首部校验和unsigned char srcipaddr4, /源ip地址 destipaddr4; /目标ip地址; 从这个首部看,ip协议首部结构体包含34个字节,其中ip首部有20个字节。根据首部可以确定一些信息,比如ip包中转载的是什么协议、ip地址、ip长度等。ip协议与arp协议的一个不同点是,ip协议中包含了一个首部校验和。这样的校验和在其他协议中也都有,而且计算方法也相同。校验和的作用是判断该协议被校验和检验过的区域(ip协议只检验首部的20个字节)是否正确,进而判断该数据包是否损坏。关于这个校验和的操作,有些芯片内部可以自动计算出来,有些却不能。为了统一,稍后会介绍校验和的计算方法。 本文所涉及的ip协议除了装载其他协议外,没有其他用途,因此也并无很复杂的原理。接下来继续介绍涉及到ip协议的函数。这里再加一些宏和变量:#define IP_TRBUF (struct ip_hdr *)&transmit_buffer0)#define IP_REBUF (struct ip_hdr *)&receive_buffer0)/宏定义:为书写方便,以ip协议首部的方式指向发送和接收的数据unsigned char m_ipid = 0;/*这是一个全局变量,可以看做是个发送ip协议包的计数器,专门用来填充ip协议首部结构体中的ipid成员*/ 接下来会完成两个函数,ip协议处理函数和ip协议数据包发送函数。前者用来完成ip协议的数据包分析和处理,后者用来发送一个ip协议数据包。具体函数如下:/ip协议处理函数unsigned long ip_process(void)/判断是否是基板的ip地址if( ( IP_REBUF-destipaddr0 != my_ipaddr0 )| ( IP_REBUF-destipaddr1 != my_ipaddr1 )| ( IP_REBUF-destipaddr2 != my_ipaddr2 )| ( IP_REBUF-destipaddr3 != my_ipaddr3 ) ) return IP_NOTMYIP;/自定义宏,表示不是基板的ip包,放弃该包。if(IP_REBUF-vhl != 0x45) return IP_BED_PACKET;/自定义宏,表示此ip包已经损坏/我们仅做了以上两个简单判断,其实可以做更详细判断,但这些已经足够了if(ip_chksum(&(IP_REBUF-vhl) != 0)/校验和函数“ip_chksum()”在后面介绍,这里暂时标记一下 return IP_BED_PACKET;/*这里将接收到的完整数据包长度减掉20字节ip协议首部和14字节以太网首部,剩余字节数作为ip协议中数据内容的字节数*/receive_packet_length = IP_REBUF-len - 34;return IP_REBUF-proto;/返回ip包中包含的协议类型,以供后面程序继续处理/发送ip数据包void ip_send(unsigned long len)/参数为ip协议包中数据部分的字节数(这个数据部分包括udp协议)/以太网首部填充memcpy(IP_TRBUF-ethhdr.d_mac, server_macaddr, 6);memcpy(IP_TRBUF-ethhdr.s_mac, my_macaddr, 6);IP_TRBUF-ethhdr.type = 0x0800;/ip协议代码,大端格式/ IP headerlen += 34;/更新数据包长度,增加ip首部和以太网首部共34字节IP_TRBUF-vhl = 0x45;/固定写法,ip版本和首部长度,我们的网络貌似还没有用到IP6IP_TRBUF-tos = 0x00;/可不关心,写0就行IP_TRBUF-len = (unsigned short)(len - 14);/ip包总长度,包括ip首部和数据(不包括以太网首部)IP_TRBUF-ipid = m_ipid;/ip包标识,也就是计数器,每次发完加1就行。m_ipid+;IP_TRBUF-ipoffset = 0x00;/可不关心IP_TRBUF-ttl = 255;/可不关心,表示此包可以经过路由器的个数,填为最大值IP_TRBUF-proto = 17;/表示ip包中装载着udp协议(大端格式),6为tcp协议memcpy(IP_TRBUF-srcipaddr, my_ipaddr, 4);/基板ip地址memcpy(IP_TRBUF-destipaddr, server_ipaddr, 4);/上位机ip地址IP_TRBUF-ipchksum = 0;/校验和字段在计算结果之前用0填充,并参与计算IP_TRBUF-ipchksum = ip_chksum(&(IP_TRBUF-vhl);/校验和的填充和检验的计算方法相同,用同一函数sendpacket(transmit_buffer, len);/发送这个数据包/*注释:值得注意的是,在这个函数中,要发送的数据包长度我用了传递参数的办法。而之前我又用了个全局变量来存放要发送的数据包长度,arp协议的实现,我使用了全局变量来传递要发送的数据包长度。其实这两个办法都可行,由于这两个函数我不是在同一个时间写的,实现方法上有些不同,现在看来是有点冗余,大家可以根据自己的书写习惯选择其中的一种方法,我就不在这里修改程序了。*/ 由于这里ip协议仅仅用于装载udp协议,所以暂时没有办法发包来验证。 4、校验和 上节ip协议中提到过校验和的概念,并且用到了一个函数“ip_chksum()”,下面简单介绍一下校验和及其实现方法。 校验和机制是一种检验数据包完整性的方法,也就是查看一下接收到的包是否坏损(数据出错)。其原理也比较简单,在发送数据包时,将校验和计算的结果放上在指定位置上。数据包发送到接收端后,接收端在发送端检验和的基础上,再进行一次检验和的计算,如果结果全“0”,则表示正确,否则表示数据包已经损坏(一般损坏的数据包就直接丢弃了)。 校验和采用一种很巧妙且简单的数学方法进行计算,使发送端和接收端校验和的计算方法相同,而且根据计算结果可以正确判断数据包中被校验的数据区域是否有损坏(但不能确定是哪个数据是损坏的)。计算方法如下:校验和占用16位(2字节)。首先,以0x0000为起始将待被验证的数据以16位宽依次取出与之相加,如果溢出就将结果再加1。依次累加下去直到完成所有数据的检验。如果数据是奇数个字节(即最后一位数据不是16位宽,只有8位)那么将此数据扩展到16位宽(高8位填充0)后再相加。这样就得到了一个基本的校验和结果。最后,将此结果取反后便得到最终可用的校验和了。说了这么多,用一句最简单的话概括:数据求和后取反(或数据取反后求和)。 下面看一下具体函数。校验和通过两部分函数组成,求和函数和取反函数,求和函数做通用校验用,取反函数被各个协议调用:/ 校验和计算函数unsigned short chksum(unsigned short *sdata, unsigned long len)/参数为需要校验的数据部分地址指针(16位宽对齐),数据长度(以字节为单位)unsigned short sum = 0;/定义一个16为宽以0x0000为开始的基数for(sum = 0; len 1; len -= 2)/用于求和累加的循环 sum += *sdata; if( sum *sdata )/判断溢出 sum+; sdata+; /数据长度是奇数个字节的情况 /*if(len = 1)/小端格式 sum += (*sdata & 0xff); if(sum (*sdata & 0xff) sum+;*/ if(len = 1)/大端格式,根据自己处理器情况选择 sum += (*sdata & 0xff00); if(sum (*sdata & 0xff00) sum+;return sum;/返回16位宽求和累加结果/ IP协议首部校验和unsigned short ip_chksum(void* sdata)return chksum(unsigned short*)sdata, 20); / UDP首部校验和unsigned short udp_chksum(void* sdata, unsigned long len)unsigned short sum = 0;unsigned short tem = 0;/ UDP首部和数据部分校验和计算sum = chksum(unsigned short*)sdata, len);/ UDP伪首部校验和计算/ 小端格式/*tem = (unsigned short)my_ipaddr1 8) & 0xff00) | (unsigned short)my_ipaddr0 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)my_ipaddr3 8) & 0xff00) | (unsigned short)my_ipaddr2 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)server_ipaddr1 8) & 0xff00) | (unsigned short)server_ipaddr0 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)server_ipaddr3 8) & 0xff00) | (unsigned short)server_ipaddr2 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)IP_UDP 8) & 0xff00;sum += tem;if(sum tem)sum+;tem = (unsigned short)len & 0xff) 8);sum += tem;if(sum tem)sum+;*/大端格式,根据自己的处理器选择tem = (unsigned short)my_ipaddr0 8) & 0xff00) | (unsigned short)my_ipaddr1 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)my_ipaddr2 8) & 0xff00) | (unsigned short)my_ipaddr3 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)server_ipaddr0 8) & 0xff00) | (unsigned short)server_ipaddr1 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)server_ipaddr2 8) & 0xff00) | (unsigned short)server_ipaddr3 & 0xff);sum += tem;if(sum tem)sum+;tem = (unsigned short)IP_UDP & 0xff;sum += tem;if(sum tem)sum+;tem = (unsigned short)len;sum += tem;if(sum sport = udp_sport;UDP_TRBUF-dport = udp_dport;/源地址和目的地址均设为0,这里不关心这个值UDP_TRBUF-len = len;UDP_TRBUF-udpchksum = 0;/检验和,上面讲过喽UDP_TRBUF-udpchksum = udp_chksum(&(UDP_TRBUF-sport), len + 8);/这句可选ip_send(len + 8);/调用ip发送函数,将数据包发出去 发送udp数据包代码很简单吧,只要填充好udp首部即可。当然,ip包发送函数也要正确。下面写个简单的main函数验证一下。 关于以下调试过程的注释:调试程序我习惯于用上串口,可以很方便的在上位机上显示调试信息,所我在main函数里加上个几个串口相关函数。这些函数仅仅用于调试,而且与处理器相关,用到时我只给出函数用途而不在写代码了。另外,上面提到的对芯片初始化等操作,在main函数里省略了,这里只挑选最关键的代码来验证。需要定义的宏和全局变量,在上面已经提到过,main函数里也不在重复。#define DATA_TRBUF (unsigned char *)&transmit_buffer28void main(void)unsigned short tem;/定义几个临时变量/*一些初始化等操作在此处进行,这里省略*/首先通过arp协议寻找上位机,这一步在以前的文章中介绍过,这里再简单写一下while(1)arp_request();/发出arp请求while(/*等待中断标志或某种标识来判断是否有数据包被接收到,没有数据包时为真,有数据包收到时为假*/);/上面的语句会根据不同的网卡芯片和不同的处理器,判断方式会有所不同。tem = receivepacket();if(tem = 0x0806)/判断是否是arp协议 if(ARP_SAVETHEREPLY = arp_process(receive_packet_length)break; /判断是否是arp应答,如果是则存储上位机mac地址后退出循环for(a = 5000000; a 0; a-);/适当的延时1秒左右send_packet_length = 4;/填写数据长度(字节数)DATA_TRBUF0 = 0xaa;/将数据填充到发送数据缓存区DATA_TRBUF1 = 0xbb;DATA_TRBUF2 = 0xcc;DATA_TRBUF3 = 0xdd;/填充四个数据”0xaa 0xbb 0xcc 0xdd”udp_send(send_packet_length);/发送udp数据包while(1);/程序停止,查看结果 上面main函数利用udp发送了四个数。实际上,main函数的作用就是将要发送的数据放到发送数据的缓存中并且指定好数据部分的长度,真正发送数据包的工作都是由前面讲到的函数来完成的。至于如何把数据放到缓存和放多少数据是由自己的程序决定的。为了简单易懂,我直接向发送数据缓存中填写了四个数值。 这里向大家推荐个软件“IRIS”用于网络调试,可以很方便的查看所有经过网卡的数据包,我们利用这个工具查看我们发出的数据包。如图1所示:图1 验证发送udp数据包PS:请不要问我要这个软件,因为这是收费的软件,我也是试用的,不过用这个软件查看数据包很方便,需要的话还是自己想办法吧。图中在“capture”栏中的右上部分是接收到的数据包(我只过滤了基板收发的数据包)。共显示三个,依次是基板发出的arp请求、上位机发出的arp应答和基板发出的包含四个数据的udp包。这与我们所写的main函数中的顺序是一致的。在“capture”栏中左半部分是当前选中数据包的解析,我选中的是第三个udp数据包,可以看到解析出的数据包结构:mac首部、ip首部、udp首部和4字节数据。这样便可以验证我们发送的udp数据包是没有错误的。另外,在红圈处我们看到udp校验和,这与ip的校验和表示的不同,我们看ip首部中校验和部分后面注释了一个“correct”标记表示校验正确,但udp的校验和没有这个标记。这并不是说udp校验和计算错误了,而是说上位机没有对udp校验和做验证,也就验证了udp校验和是可选的,在默认情况下是关闭的。 我们继续讲一下如何接收一个udp包。但从上面所讲的内容来看,我们队端口并不关心,如果也不验证检验和的话,对udp包并不需要特别处理。所以这里我就不写udp包的处理函数,而直取出数据。下面,修改一下刚写好的main函数如下:#define DATA_REBUF (unsigned char *)&transmit_buffer28void main(void)unsigned long a;/定义几个临时变量/*一些初始化等操作在此处进行,这里省略*/for(a = 10000000; a 0; a-);/延时printf(rnrnrnrnrnrnrnrnSend ARP request . );/用串口打印一串字符,这是根据自己的处理器自行添加的函数,用于查看调试信息/首先通过arp协议寻找上位机while(1)arp_request();/发出arp请求while(/*等待中断标志或某种标识来判断是否有数据包被接收到,没有数据包时为真,有数据包收到时为假*/);/上面的语句会根据不同的网卡芯片和不同的处理器,判
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 污水处理站工程施工合同(3篇)
- 电脑病毒与网络安全课件
- 2025-2026学年上海市上册七年级数学期中考试卷 有答案
- 风花雪月的营销方案
- 凉生营销方案
- 大学活动赞助策划方案
- 卡部营销方案
- 影视制作后期知识自测题集及解析
- 幼儿园面试题卡设计思路及答案解析
- 合伙摆烧烤摊协议书
- 创业思维与现代企业的核心竞争力
- 《光伏发电组件工艺》课件
- 【MOOC】新媒体设计之图形图像处理-福建师范大学 中国大学慕课MOOC答案
- 铁道运营专业职业生涯规划
- 黄工为什么走?案例分析
- 血脂管理指导
- 《卧式拉力试验机》
- (完整版)医学英语(阅读一分册)翻译及答案
- 胰岛干细胞分化和定向
- 心电图危急值的识别与处理
- 舞蹈疗法行业商业机会挖掘与战略布局策略研究报告
评论
0/150
提交评论