当前位置:首页 > 单片机 > 单片机
[导读]1前言 UDP协议全称为用户数据协议,是一种简单有效的运输协议。和以太网首部和IP首部相似,UDP首部也有自身的数据结构定义。从运输协议开始引入端口的概念,端口相当于一个应用程序的标识符。相对于TCP协议而言,UD

1前言
UDP协议全称为用户数据协议,是一种简单有效的运输协议。和以太网首部和IP首部相似,UDP首部也有自身的数据结构定义。从运输协议开始引入端口的概念,端口相当于一个应用程序的标识符。相对于TCP协议而言,UDP协议简单的多。本文将实现UDP协议,并通过几个简单的案例说明UDP的使用。
1.2 相关资料
ENC28J60学习笔记链接
http://www.amobbs.com/thread-5519381-1-1.html
AVRNET学习笔记 ETHERNET和ARP部分
http://www.amobbs.com/thread-5519452-1-1.html
AVRNET学习笔记 IP和ICMP部分
http://www.amobbs.com/thread-5519494-1-1.html
AVRNET项目(国外)
http://www.avrportal.com/?page=avrnet
AVR webserver项目(国外)
http://www.tuxgraphics.org/electronics/200611/article06111.shtml#0lfindex0
2 UDP部分实现
UDP功能的实现可分为UDP首部填充,UDP缓冲区填充和UDP报文查询。UDP首部填充是一个按部就班的过程,即填充源端口,目标端口,长度和校验和。UDP缓冲区填充即往UDP负载部分逐个填充数据。UDP报文查询功能即匹配本机UDP端口号,并进行函数处理。为了实现这些功能,首先需要以下宏定义。需要注意的是以太网传输协议中数据被以大端的形式保存,即低地址存放了高字节。例如端口号的高字节存放在了0x22的位置,而端口号的低字节存放在了0x23的位置。

// UDP默认端口号

#define UDP_AVR_PORT_V 3000

#define UDP_AVR_PORT_H_V(UDP_AVR_PORT_V>>8)

#define UDP_AVR_PORT_L_V(UDP_AVR_PORT_V&0xff)

// 源端口

#define UDP_SRC_PORT_H_P0x22

#define UDP_SRC_PORT_L_P0x23

// 目标端口

#define UDP_DST_PORT_H_P0x24

#define UDP_DST_PORT_L_P0x25

// UDP负载长度

#define UDP_LENGTH_H_P 0x26

#define UDP_LENGTH_L_P 0x27

// UDP校验和

#define UDP_CHECKSUM_H_P0x28

#define UDP_CHECKSUM_L_P0x29

// UDP负载起始地址

#define UDP_DATA_P 0x2A


2.1 UDP首部填充
UDP首部填充中需要明确UDP的端口号,AVRNET项目中通过常数宏定义实现
#define UDP_AVR_PORT_V 3000
#define UDP_AVR_PORT_H_V(UDP_AVR_PORT_V>>8)
#define UDP_AVR_PORT_L_V(UDP_AVR_PORT_V&0xff)
从这段代码中可以看出,AVRNET的UDP端口号为3000。

void udp_generate_header ( BYTE *rxtx_buffer, WORD_BYTES dest_port, WORD_BYTES length )

{

WORD_BYTES ck;


// 默认端口号 3000

rxtx_buffer[UDP_SRC_PORT_H_P] = UDP_AVR_PORT_H_V;

rxtx_buffer[UDP_SRC_PORT_L_P] = UDP_AVR_PORT_L_V;


// 目标端口地址

rxtx_buffer[UDP_DST_PORT_H_P] = dest_port.byte.high;

rxtx_buffer[UDP_DST_PORT_L_P] = dest_port.byte.low;


// 负载长度

rxtx_buffer[UDP_LENGTH_H_P] = length.byte.high;

rxtx_buffer[UDP_LENGTH_L_P] = length.byte.low;


// 计算校验和

rxtx_buffer[UDP_CHECKSUM_H_P] = 0;

rxtx_buffer[UDP_CHECKSUM_L_P] = 0;

// length+8 for source/destination IP address length (8-bytes)

ck.word = software_checksum ( (BYTE*)&rxtx_buffer[IP_SRC_IP_P], length.word+8, length.word+IP_PROTO_UDP_V);

rxtx_buffer[UDP_CHECKSUM_H_P] = ck.byte.high;

rxtx_buffer[UDP_CHECKSUM_L_P] = ck.byte.low;

}

复制代码

2.2 UDP负载长度查询
UDP首部中包含UDP长度的描述字节,长度占有两个字节并以大端格式保存,由于宏定义的提示作用,弱化了大端格式的影响。长度中也包括了UDP首部的长度,UDP首部的长度为固定的8字节。

WORD udp_get_dlength( BYTE *rxtx_buffer )

{

WORD length = 0;

// 获得UDP长度

length = rxtx_buffer[UDP_LENGTH_H_P] << 8 | rxtx_buffer[UDP_LENGTH_L_P];

// 去除首部长度

length = length - 8;


return length;

}


2.3 UDP负载区填充
UDP负载去填充即在UDP首部之后填充有用的数据。在这段真实负载之前包括了UDP首部,IP首部和以太网首部,分别占用了8字节,20字节和14字节。UDP负载的起始地址通过宏由UDP_DATA_P定义。对于AVR单片机的特点,为了尽一切可能节约内存使用率,在向负载区填充数据时用到了两个函数,udp_puts_data函数操作的是BYTE*类型的数据,而udp_puts_data_p操作的为PGM_P类型数据,即位于FLASH中的数据,需要通过pgm_read_byte取出。而其他类型的CPU,例如STM却没有该功能,则使用udp_puts_data即可。

WORD udp_puts_data ( BYTE *rxtx_buffer, BYTE *data, WORD offset )

{

while( *data )

{

rxtx_buffer[ UDP_DATA_P + offset ] = *data++;

offset++;

}


return offset;

}

WORD udp_puts_data_p ( BYTE *rxtx_buffer, PGM_P data, WORD offset )

{

BYTE ch;


while( (ch = pgm_read_byte(data++)) )

{

rxtx_buffer[ UDP_DATA_P + offset ] = ch;

offset++;

}


return offset;

}


2.4 UDP报文查询
UDP报文的查询需要匹配接收数据包中的UDP端口号,若匹配成功则可对输入数据包进行处理,这些处理包括解析数据包的数据格式,分析出控制命令或查询命令。也可以通过udp_puts_data向发送缓冲区中填写响应数据。接着逐步生成以太网首部,IP首部和UDP首部,以太网首部中包含目标MAC地址,IP首部中包含目标IP地址,UDP首部中包含目标端口号。

BYTE udp_receive ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip )

{

WORD dlength = 0;

// udp负载长度

WORD udp_loadlen = 0;


// 匹配UDP协议 UDP端口号

if ( rxtx_buffer[IP_PROTO_P] != IP_PROTO_UDP_V || rxtx_buffer[UDP_DST_PORT_H_P] != UDP_AVR_PORT_H_V || rxtx_buffer[ UDP_DST_PORT_L_P ] != UDP_AVR_PORT_L_V )

return 0;


// 加入处理函数


// 生成以太网首部

eth_generate_header (rxtx_buffer, (WORD_BYTES){ETH_TYPE_IP_V}, dest_mac );

// 生成IP首部

ip_generate_header (rxtx_buffer, (WORD_BYTES){sizeof(IP_HEADER)+sizeof(UDP_HEADER)+dlength}, IP_PROTO_UDP_V, dest_ip );

// 生成UDP首部

udp_generate_header (rxtx_buffer, (WORD_BYTES){(rxtx_buffer[UDP_SRC_PORT_H_P]<<8)|rxtx_buffer[UDP_SRC_PORT_L_P]}, (WORD_BYTES){sizeof(UDP_HEADER)+dlength});

// 发送所有首部和UDP负载数据

enc28j60_packet_send ( rxtx_buffer, sizeof(ETH_HEADER)+sizeof(IP_HEADER)+sizeof(UDP_HEADER)+dlength );


return 1;

}


3 实验
实验部分主要是为了验证UDP协议,通过PC机上的网络调试软件开辟一个PC机的UDP端口,假定端口号为3001,IP地址为192.168.1.102;AVR的UDP默认端口号为3000,IP地址为192.168.1.105。
3.1 程序结构
在运行UDP程序之前,需要运行ARP,IP和ICMP各部分,并保存发起发的MAC地址和IP地址。

/* 获得新的IP报文 */

plen = enc28j60_packet_receive( (BYTE*)&rxtx_buffer, MAX_RXTX_BUFFER );

if(plen==0) return;


/* 保存客服端的MAC地址 */

memcpy ( (BYTE*)&client_mac, &rxtx_buffer[ ETH_SRC_MAC_P ], sizeof(MAC_ADDR) );

/* 检查该报文是不是ARP报文 */

if ( arp_packet_is_arp( rxtx_buffer, (WORD_BYTES){ARP_OPCODE_REQUEST_V} ) )

{

/* 向客户端返回ARP报文 */

arp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac );

return;

}


/* 保存客服端的IP地址 */

memcpy ( (BYTE*)&client_ip, &rxtx_buffer[ IP_SRC_IP_P ], sizeof(IP_ADDR) );

/* 检查该报文是否为IP报文 */

if ( ip_packet_is_ip ( (BYTE*)&rxtx_buffer ) == 0 )

{

return;

}


/* 如果是ICMP报文 向发起方返回数据 */

if ( icmp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac, (BYTE*)&client_ip ) )

{

return;

}


// 进行UDP处理

if (udp_receive ( (BYTE *)&rxtx_buffer, (BYTE *)&client_mac, (BYTE *)&client_ip ))

{

return;

}


3.2 源IP和源端口
使用网路调试助手发送一个名称,例如UDP。在仿真环境中通过串口打印出发起方的IP地址和端口号,例如PC机的端口号设定为3001,PC机的IP地址为192.168.1.102。在udp_receive函数中需要判断UDP端口号和目标IP地址是否匹配,若匹配即可加入以下代码。首先获得UDP的负载长度,使用memcpy命令复制到udp_recbuf中,接着通过串口打印源IP地址,该参数位于IP首部,源端口号,该参数位于UDP首部中。

// 获得UDP负载长度

udp_loadlen = udp_get_dlength(rxtx_buffer);


// 复制UDP负载

memcpy(udp_recbuf,(char*)&rxtx_buffer[UDP_DATA_P],udp_loadlen);


#ifdef UDP_DEBUG

printf("UDP Message!n");

printf("Send Form:%d.%d.%d.%d ",

rxtx_buffer[IP_SRC_IP_P+0],rxtx_buffer[IP_SRC_IP_P+1],

rxtx_buffer[IP_SRC_IP_P+2],rxtx_buffer[IP_SRC_IP_P+3]);

printf("Port:%dn",(rxt

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭