简单的ping6程序的实现内有代码
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Ping6程序的实现
Ping6函数主要用于向一个节点发送回送请求报文并接收该节点回复的回送应答报文用于确定一个节点的可达性及往返时间延迟。
回送请求报文格式如下:
标识符和序列号由源节点产生,用于将请求报文和应答报文对应起来,其中数据可以是任意字节的任意数据。
回送应答报文格式如下:
回送应答报文的标识符和序列号及数据都是由请求报文中获得。
在linux下ICMPv6的报文头结构如下:
本程序中,报文简化为只包含消息类型、消息代码、数据报的ID、数据报的序列号及数据段几个部分。
ICMPV6回送请求报文类型为128;
ICMPV6回送请求报文代码值为0;
ICMPV6回送请求报文序列号通常为一个递增的数生成
ICMPV6回送请求报文的ID用于对应回送应答报文,通常用进程的PID补充
同时保存发送时间。
int icmpv6_pack(int pack_num)
{
int i,packsize;
struct icmp6_hdr *icmpv6;
struct timeval *tval;
icmpv6=(struct icmp6_hdr*)sendbuf;
icmpv6->icmp6_type=128; //消息类型为ICMPv6回显请求icmpv6->icmp6_code=0; //code为0
icmpv6->icmp6_cksum=0; //校验和初始值为0
icmpv6->icmp6_seq=pack_num; //序列号
icmpv6->icmp6_id=pid; //进程pid
packsize=8+datalen;
gettimeofday(sendtime,NULL); //获取发送数据时间
return packsize;
}
发送报文:将打包好的数据通过原始套接字发送到指定地址,使用sendto函数。每次发送成功后序列号增加1,即nsend++
void icmp6_send()
{
int packetsize;
if(nsend < max_loop_num)
{
nsend++; //发送序列号加1
packetsize =icmpv6_pack(nsend); //打包数据
if(sendto(socksaw,sendbuf,packetsize,0,(struct sockaddr *)&send_addr,
sizeof(send_addr))<0) //发送数据包
{
perror("sento error");
}
}
}
接受报文:接受报文在接收数据包的值小于发送数据包的值时,继续接收数据包,通过recvfrom函数将接收到的数据存储到recvbuf中,将发送数据端的IP地址存储在recv_addr 中,记录接收数据包的时间,调用unpack函数对数据包进行解包和数据分析。接收到一个报文后接收序列号并没有加1,而是在解包后确定数据包正确后加1,防止接收错误的报文导致丢包的情况。
void icmp6_recv()
{ int n,fromlen;
fromlen =sizeof(recv_addr);
while(nrecv if((n = recvfrom(socksaw,recvbuf,sizeof(recvbuf),0, (struct sockaddr *)&recv_addr,&fromlen))<0) //接收报文 { perror("recvfrom error"); } gettimeofday(&recvtime,NULL); //保存接收数据包时间 unpack(recvbuf,n); //解压数据包,并数据处理} } 解压收到的报文:接收到报文后判断报文长度是不是太短,如果不是证明报文有效,需要判断报文的类型是否为回送应答报文即类型号是不是129,并核实其标识ID是否是本进程PID,确定报文正确后,接收序列号加1.通过接收报文的时间减去发送报文的时间,可以得到报文 往返时间。最后是对返回信息进行显示,因为ipv6为128个字节,中间为0的位一般用::代替,为了显示美观,采用了标志位flag进行判断,达到显示标准ipv6地址格式的目的。int unpack(char *buf,int len) { int i; int iphdrlen; struct ip6 *ip6; struct icmp6_hdr *icmpv6; double rtt; int flag=0; icmpv6 = (struct icmp6_hdr *)(buf); //获得ICMPv6报文地址 if(len<8) { printf("ICMP packer\'s length is less than 8\n"); return(-1); }/*检查消息类型和进程号是否匹配*/ if((icmpv6->icmp6_type ==129)&&(icmpv6->icmp6_id == pid)) { nrecv++; //判断数据有效,接收序列号加1 _sec = _sec - sendtime->tv_sec; _usec = _usec - sendtime->tv_usec; //计算时间差 rtt = _sec*1000.0 + _usec/1000.0; printf("%d bytes from:",len); for(i=0;i<16;i++) //打印ipv6地址 { if(recv_addr.sin6_addr.s6_addr[i]==0&&flag==0) {printf("::"); flag=1;} else if(recv_addr.sin6_addr.s6_addr[i]==0&&flag==1) {flag=flag;} else if(recv_addr.sin6_addr.s6_addr[i]!=0&&flag==1) {printf("%02x",recv_addr.sin6_addr.s6_addr[i]); flag=2; } else {printf("%02x",recv_addr.sin6_addr.s6_addr[i]); } } printf(" icmp_seq=%u time=%1.4f ms\n",icmpv6->icmp6_seq,rtt); } } 主函数:主函数主要包括以下几个部分: (1)对参数个数进行判断,判断参数是否有效。 (2)通过套接字选项设置发送和接收超时选项。 (3)进行地址处理,首先通过inet_pton函数将字符串格式的地址转换为二进制地址,然