- ----------------------------------------------------
| | 状态(status) | 次数(times) | 内存句柄(memhandle) |
| ----------------------------------------------------
N个| 。。。 |
| ----------------------------------------------------
| | 状态(status) | 次数(times) | 内存句柄(memhandle) |
- ----------------------------------------------------
N=MaxLenPingBuf
图3 pingbuf记录缓冲区格式
IP地址要先转换成MAC地址才能在以太网上传输,如果ARP缓存里没有对应项,则需要较长时间查找(网络传输时间相对于CPU时间)。为了不阻塞进程,我设计了一个pingbuf记录缓冲区,如图3所示。此表1秒钟被查询一次,根据当前情况改变状态。它包括状态、次数、内存句柄三个域。“次数”字段指示测试剩余数,为0表示测完,同时改变状态为0以表明此记录项现在空闲。“内存句柄”字段保存ICMP报文缓冲区首址指针。“状态”字段记录状态号,含义如下:
0---空闲
1---已发出但无应答
2---已发出且应答
3---等ARP
4---第一次准备发(用于同步1秒时钟)
状态变迁图如图4所示。
图4略,详见伪代码清单PingCycle,请自行画出状态图。
这个实现里还使用了ping命令处理、IP地址转换、校验和计算等辅助程序,详见伪代码清单。IP协议使用统一的CheckSum算法计算和验证数据报的首部校验和。将首部视为一个16位的整数序列,并定义校验和是首部中所有16位整数的和的二进制反码。同样和数及补码也被定义使用二进制反码算法。所有TCP/IP协议的校验和计算和数据包的校验均由CheckSum子程序完成。不过需要注意的是TCP和UDP的校验需要加上伪头部。需要首部校验和计算及验证的包为:IP、ICMP、UDP、TCP。相互间的差别仅在于求和数据不同,算法都采用CheckSum。详见源程序清单。(提示:IP包头从版本号、首部长度、服务类型到目的站IP地址(如果不含IP选项)共20字节;ICMP校验和只覆盖ICMP报文。对比UDP、TCP伪首部和IP首部相似点,可以不必单独分配伪首部缓冲区,而直接利用IP缓冲区计算校验和。观察知IP头邻接数据,采取一定措施可实现直接计算。即先将IP寿命字段清0,协议字段赋相应值,校验和赋UDP/TCP包长度值,并加上12,表示伪首部的3长字长度,完成计算后向IP包首部添入正确寿命、校验和值,见图5。)
0 8 16 31 0 8 16 31
---------------------------- ---------------------------
| 寿命 | 协议 | 首部校验和 | | 源站IP地址 |
---------------------------- ---------------------------
| 源站IP地址 | | 目的站IP地址 |
---------------------------- ---------------------------
| 目的站IP地址 | | 零 | 协议 | UDP/TCP长度 |
---------------------------- ---------------------------
| 数据 | | UDP/TCP包数据 |
---------------------------- ---------------------------
IP包格式(局部) UDP、TCP伪首部+数据格式
图5 IP包格式(局部)和UDP、TCP伪首部格式对比图
伪代码清单:
PingRequest(IP地址) //Ping请求
{
//申请小号内存
pICMP=OSMemGet();
//填写以太网帧
目的MAC地址=ping命令传入的IP地址解析后得到的物理地址
源MAC地址=本节点MAC地址
类型=0x0800 //IP包
//填写IP帧
版本号&首部长度=0x45
服务类型=0
总长度=60
标识符=GlobalID++ //全局变量16位GlobalID加1
标志&分片偏移量=0x0000
寿命=0x80
协议=0x0001 //icmp
首部校验和=0x0000
源IP地址=本节点IP地址
目的IP地址=ping命令传入的IP地址
首部校验和=CheckSum(IP首部和长度) //计算IP包首部校验和
//填写ICMP帧
类型=8 //8 请求 0 应答
代码=0
校验和=0x0
标识符=0x0300
序号=GlobalID
校验和=CheckSum(ICMP首部和长度) //计算ICMP包首部校验和
//将ICMP包登记在PingBuf中
for(i=0;i<MaxLenPingBuf;i++){
if(PingBuf.status==0){
PingBuf.times=0x8; //测试8次
PingBuf.ip=ping命令传入的IP地址;
PingBuf.memhandle=内存句柄;
PingBuf.status=4; //第一次准备发(用于同步1秒时钟)
break;
}
}
if(i==MaxLenPingBuf) 释放内存;
}
PingAnswer(*输入包缓冲区首址pBUF) //Ping应答
{
//改写以太网帧
目的MAC地址=源MAC地址
源MAC地址=本节点MAC地址
//改写IP帧
寿命=寿命-1
目的IP地址=源IP地址
源IP地址=本节点IP地址
首部校验和=0x0000
首部校验和=CheckSum(IP首部和长度) //计算IP包首部校验和
//改写ICMP帧
类型=0 //8 请求 0 应答
校验和=0x0
校验和=CheckSum(ICMP首部和长度) //计算ICMP包首部校验和
//直接发送ICMP包至TxQFIFO缓存
OSQSend(QID,*pBUF);
}
PingEcho(*输入包缓冲区首址pBUF) //收到应答后回显
{
//打印ping响应,因为51定时器较慢,time参数省略(time是本机与对方主机往返一次所用时间)。
PrintStr("\n\tReply from IP=");
PrintStr(输入包之源IP地址);
PrintStr(": bytes=32");
PrintStr(" TTL=");
PrintByte(输入包之TTL);
PrintStr("\n");
//处理PingBuf的记录
for(i=0;i<MaxLenPingBuf;i++){
if(PingBuf.status==1){
if(PingBuf.ip==pBUF.ipframe.ip){
PingBuf.status=2; //已发出且应答
break;
}
}
}
}
PingCycle() //定时操作,放在1秒循环任务中
{
for(;;){
taskDelay(1秒);
for(i=0;i<MaxLenPingBuf;i++){
switch(PingBuf.status)
case 0: //空闲
break;
{
case 1: //已发出但无应答
//打印超时信息
PrintStr("\n\tRequest timed out.(");
PrintStr(PingBuf.ip);
PrintStr(")\n");
case 2: //已发出且应答
//状态变迁
PingBuf.times=PingBuf.times-1;
if(PingBuf.times==0)
PingBuf.status=0;
else{
case 4: //第一次准备发(用于同步1秒时钟)
//查ARP缓存
if(ARP缓存有对应项){
//直接发送ICMP包至TxQFIFO缓存
OSQSend(QID,*pBUF);
PingBuf.status=1; //已发出但无应答
}
else PingBuf.status=3; //等ARP
}
break;
}
case 3: //等ARP
{
//查ARP缓存
if(ARP缓存有对应项){
//直接发送ICMP包至TxQFIFO缓存
OSQSend(QID,*pBUF);
}
PingBuf.status=1; //已发出但无应答
}
default: //其他状态,错误
PingBuf.status=0;
}
}
}
void PingCommand(WORDTABLE *WordTable)//PING命令处理
{
if(WordTable->Num==1)
PrintStr("\nPlease input IP address!\n\n");
else{
if(IPadrToHEX(WordTable->wt[1].Str,&ping_ip_address)==0){
PrintStr("\nIP address error!\n\n");return;
}
else
PingRequest(ping_ip_address);
}
}
INT16U CheckSum(INT16U *buf,INT16U length) //校验和计算
{
INT16U len;
INT32U sum;
len=length>>1;
for(sum=0;len>0;len--)
sum+=*buf++;
if(length&1)
sum+=(*buf&0xFF00);
sum=(sum>>16)+(sum&0xFFFF);
sum+=(sum>>16);
return(~sum);
}
union ip_address_type{ //ip地址数据结构
unsigned long dwords;
unsigned int words[2];
unsigned char bytes[4];
};
bit IPadrToHEX(unsigned char *Str,union ip_address_type *ip) //IP字符串转换到IP地址值
{
unsigned char i,j,ch,x;
ch=*Str++;
for(j=0;j<3;j++){
x=0;
for(i=0;i<4;i++){
if(ch=='.') {ch=*Str++;break;}
else if(i==3) return 0;
else if(ch<0&&ch>9) return 0;
else
x=10*x+(ch-'0');
ch=*Str++;
}
ip->bytes[j]=x;
}
x=0;
for(i=0;i<4;i++){
if(ch=='\0') {ch=*Str++;break;}
else if(i==3) return 0;
else if(ch<0&&ch>9) return 0;
else
x=10*x+(ch-'0');
ch=*Str++;
}
ip->bytes[3]=x;
return 1;
}
void HEXToIPadr(unsigned char *Str,union ip_address_type *ip) //IP地址值转换到IP字符串
{
unsigned char i;
unsigned char x,y;
for(i=0;i<4;i++){
x=ip->bytes;
if(x>99){
y=x/100;*Str++=y+'0';
x=x-100*y;y=x/10;*Str++=y+'0';
x=x-10*y;*Str++=x+'0';
if(i==3) *Str++='\0';
else *Str++='.';
}
else if(x>9){
y=x/10;*Str++=y+'0';
x=x-10*y;*Str++=x+'0';
if(i==3) *Str++='\0';
else *Str++='.';
}
else{
*Str++=x+'0';
if(i==3) *Str++='\0';
else *Str++='.';
}
}
}
参考文献:
1。《用TCP/IP进行网际互连》(第3版)第一、二、三卷 DOUGLAS E.COMER著 电子工业出版社
作者<asdjf@163.com>
文章评论(0条评论)
登录后参与讨论