用C语言实现Ping程序Word格式文档下载.docx

上传人:b****6 文档编号:17025671 上传时间:2022-11-27 格式:DOCX 页数:16 大小:24.85KB
下载 相关 举报
用C语言实现Ping程序Word格式文档下载.docx_第1页
第1页 / 共16页
用C语言实现Ping程序Word格式文档下载.docx_第2页
第2页 / 共16页
用C语言实现Ping程序Word格式文档下载.docx_第3页
第3页 / 共16页
用C语言实现Ping程序Word格式文档下载.docx_第4页
第4页 / 共16页
用C语言实现Ping程序Word格式文档下载.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

用C语言实现Ping程序Word格式文档下载.docx

《用C语言实现Ping程序Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《用C语言实现Ping程序Word格式文档下载.docx(16页珍藏版)》请在冰豆网上搜索。

用C语言实现Ping程序Word格式文档下载.docx

{

#if__BYTE_ORDER==__LITTLE_ENDIAN

unsignedintip_hl:

4;

/*headerlength*/

unsignedintip_v:

/*version*/

#endif

#if__BYTE_ORDER==__BIG_ENDIAN

u_int8_tip_tos;

/*typeofservice*/

u_shortip_len;

/*totallength*/

u_shortip_id;

/*identification*/

u_shortip_off;

/*fragmentoffsetfield*/

#defineIP_RF0x8000/*reservedfragmentflag*/

#defineIP_DF0x4000/*dontfragmentflag*/

#defineIP_MF0x2000/*morefragmentsflag*/

#defineIP_OFFMASK0x1fff/*maskforfragmentingbits*/

u_int8_tip_ttl;

/*timetolive*/

u_int8_tip_p;

/*protocol*/

u_shortip_sum;

/*checksum*/

structin_addrip_src,ip_dst;

/*sourceanddestaddress*/

};

其中ping程序只使用以下数据:

∙IP报头长度IHL(InternetHeaderLength)?

D?

D以4字节为一个单位来记录IP报头的长度,是上述IP数据结构的ip_hl变量。

∙生存时间TTL(TimeToLive)?

D以秒为单位,指出IP数据报能在网络上停留的最长时间,其值由发送方设定,并在经过路由的每一个节点时减一,当该值为0时,数据报将被丢弃,是上述IP数据结构的ip_ttl变量。

回页首

ICMP报头格式

ICMP报文分为两种,一是错误报告报文,二是查询报文。

每个ICMP报头均包含类型、编码和校验和这三项内容,长度为8位,8位和16位,其余选项则随ICMP的功能不同而不同。

Ping命令只使用众多ICMP报文中的两种:

"

请求回送'

(ICMP_ECHO)和"

请求回应'

(ICMP_ECHOREPLY)。

在Linux中定义如下:

#defineICMP_ECHO0

#defineICMP_ECHOREPLY8

这两种ICMP类型报头格式如下:

在Linux中ICMP数据结构(<

netinet/ip_icmp.h>

structicmp

{

u_int8_ticmp_type;

/*typeofmessage,seebelow*/

u_int8_ticmp_code;

/*typesubcode*/

u_int16_ticmp_cksum;

/*onescomplementchecksumofstruct*/

union

u_charih_pptr;

/*ICMP_PARAMPROB*/

structin_addrih_gwaddr;

/*gatewayaddress*/

structih_idseq/*echodatagram*/

u_int16_ticd_id;

u_int16_ticd_seq;

}ih_idseq;

u_int32_tih_void;

/*ICMP_UNREACH_NEEDFRAG--PathMTUDiscovery(RFC1191)*/

structih_pmtu

u_int16_tipm_void;

u_int16_tipm_nextmtu;

}ih_pmtu;

structih_rtradv

u_int8_tirt_num_addrs;

u_int8_tirt_wpa;

u_int16_tirt_lifetime;

}ih_rtradv;

}icmp_hun;

#defineicmp_pptricmp_hun.ih_pptr

#defineicmp_gwaddricmp_hun.ih_gwaddr

#defineicmp_idicmp_hun.ih_idseq.icd_id

#defineicmp_seqicmp_hun.ih_idseq.icd_seq

#defineicmp_voidicmp_hun.ih_void

#defineicmp_pmvoidicmp_hun.ih_pmtu.ipm_void

#defineicmp_nextmtuicmp_hun.ih_pmtu.ipm_nextmtu

#defineicmp_num_addrsicmp_hun.ih_rtradv.irt_num_addrs

#defineicmp_wpaicmp_hun.ih_rtradv.irt_wpa

#defineicmp_lifetimeicmp_hun.ih_rtradv.irt_lifetime

struct

u_int32_tits_otime;

u_int32_tits_rtime;

u_int32_tits_ttime;

}id_ts;

structipidi_ip;

/*optionsandthen64bitsofdata*/

}id_ip;

structicmp_ra_addrid_radv;

u_int32_tid_mask;

u_int8_tid_data[1];

}icmp_dun;

#defineicmp_otimeicmp_dun.id_ts.its_otime

#defineicmp_rtimeicmp_dun.id_ts.its_rtime

#defineicmp_ttimeicmp_dun.id_ts.its_ttime

#defineicmp_ipicmp_dun.id_ip.idi_ip

#defineicmp_radvicmp_dun.id_radv

#defineicmp_maskicmp_dun.id_mask

#defineicmp_dataicmp_dun.id_data

};

使用宏定义令表达更简洁,其中ICMP报头为8字节,数据报长度最大为64K字节。

1.校验和算法?

D这一算法称为网际校验和算法,把被校验的数据16位进行累加,然后取反码,若数据字节长度为奇数,则数据尾部补一个字节的0以凑成偶数。

此算法适用于IPv4、ICMPv4、IGMPV4、ICMPv6、UDP和TCP校验和,更详细的信息请参考RFC1071,校验和字段为上述ICMP数据结构的icmp_cksum变量。

2.标识符?

D用于唯一标识ICMP报文,为上述ICMP数据结构的icmp_id宏所指的变量。

3.顺序号?

Dping命令的icmp_seq便由这里读出,代表ICMP报文的发送顺序,为上述ICMP数据结构的icmp_seq宏所指的变量。

Ping命令中需要显示的信息,包括icmp_seq和ttl都已有实现的办法,但还缺rtt往返时间。

为了实现这一功能,可利用ICMP数据报携带一个时间戳。

使用以下函数生成时间戳:

#include

intgettimeofday(structtimeval*tp,void*tzp)

其中timeval结构如下:

structtimeval{

longtv_sec;

longtv_usec;

}

其中tv_sec为秒数,tv_usec微秒数。

在发送和接收报文时由gettimeofday分别生成两个timeval结构,两者之差即为往返时间,即ICMP报文发送与接收的时间差,而timeval结构由ICMP数据报携带,tzp指针表示时区,一般都不使用,赋NULL值。

数据统计

系统自带的ping命令当它接送完所有ICMP报文后,会对所有发送和所有接收的ICMP报文进行统计,从而计算ICMP报文丢失的比率。

为达此目的,定义两个全局变量:

接收计数器和发送计数器,用于记录ICMP报文接受和发送数目。

丢失数目=发送总数-接收总数,丢失比率=丢失数目/发送总数。

现给出模拟Ping程序功能的代码如下:

/***********************************************************

*作者:

梁俊辉*

*时间:

2001年10月*

*名称:

myping.c*

*说明:

本程序用于演示ping命令的实现原理*

***********************************************************/

#include<

stdio.h>

signal.h>

arpa/inet.h>

sys/types.h>

sys/socket.h>

unistd.h>

netinet/in.h>

netdb.h>

setjmp.h>

errno.h>

#definePACKET_SIZE4096

#defineMAX_WAIT_TIME5

#defineMAX_NO_PACKETS3

charsendpacket[PACKET_SIZE];

charrecvpacket[PACKET_SIZE];

intsockfd,datalen=56;

intnsend=0,nreceived=0;

structsockaddr_indest_addr;

pid_tpid;

structsockaddr_infrom;

structtimevaltvrecv;

voidstatistics(intsigno);

unsignedshortcal_chksum(unsignedshort*addr,intlen);

intpack(intpack_no);

voidsend_packet(void);

voidrecv_packet(void);

intunpack(char*buf,intlen);

voidtv_sub(structtimeval*out,structtimeval*in);

voidstatistics(intsigno)

{printf("

\n--------------------PINGstatistics-------------------\n"

);

printf("

%dpacketstransmitted,%dreceived,%%%dlost\n"

nsend,nreceived,

(nsend-nreceived)/nsend*100);

close(sockfd);

exit

(1);

}

/*校验和算法*/

unsignedshortcal_chksum(unsignedshort*addr,intlen)

{intnleft=len;

intsum=0;

unsignedshort*w=addr;

unsignedshortanswer=0;

/*把ICMP报头二进制数据以2字节为单位累加起来*/

while(nleft>

1)

{sum+=*w++;

nleft-=2;

}

/*若ICMP报头为奇数个字节,会剩下最后一字节。

把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/

if(nleft==1)

{*(unsignedchar*)(&

answer)=*(unsignedchar*)w;

sum+=answer;

sum=(sum>

>

16)+(sum&

0xffff);

sum+=(sum>

16);

answer=~sum;

returnanswer;

/*设置ICMP报头*/

intpack(intpack_no)

{inti,packsize;

structicmp*icmp;

structtimeval*tval;

icmp=(structicmp*)sendpacket;

icmp->

icmp_type=ICMP_ECHO;

icmp_code=0;

icmp_cksum=0;

icmp_seq=pack_no;

icmp_id=pid;

packsize=8+datalen;

tval=(structtimeval*)icmp->

icmp_data;

gettimeofday(tval,NULL);

/*记录发送时间*/

icmp_cksum=cal_chksum((unsignedshort*)icmp,packsize);

/*校验算法*/

returnpacksize;

/*发送三个ICMP报文*/

voidsend_packet()

{intpacketsize;

while(nsend<

MAX_NO_PACKETS)

{nsend++;

packetsize=pack(nsend);

/*设置ICMP报头*/

if(sendto(sockfd,sendpacket,packetsize,0,

(structsockaddr*)&

dest_addr,sizeof(dest_addr))<

0)

{perror("

sendtoerror"

continue;

sleep

(1);

/*每隔一秒发送一个ICMP报文*/

/*接收所有ICMP报文*/

voidrecv_packet()

{intn,fromlen;

externinterrno;

signal(SIGALRM,statistics);

fromlen=sizeof(from);

while(nreceived<

nsend)

{alarm(MAX_WAIT_TIME);

if((n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,

from,&

fromlen))<

0)

{if(errno==EINTR)continue;

perror("

recvfromerror"

gettimeofday(&

tvrecv,NULL);

/*记录接收时间*/

if(unpack(recvpacket,n)==-1)continue;

nreceived++;

/*剥去ICMP报头*/

intunpack(char*buf,intlen)

{inti,iphdrlen;

structip*ip;

structtimeval*tvsend;

doublertt;

ip=(structip*)buf;

iphdrlen=ip->

ip_hl<

<

2;

/*求ip报头长度,即ip报头的长度标志乘4*/

icmp=(structicmp*)(buf+iphdrlen);

/*越过ip报头,指向ICMP报头*/

len-=iphdrlen;

/*ICMP报头及ICMP数据报的总长度*/

if(len<

8)/*小于ICMP报头长度则不合理*/

{printf("

ICMPpackets\'

slengthislessthan8\n"

return-1;

/*确保所接收的是我所发的的ICMP的回应*/

if((icmp->

icmp_type==ICMP_ECHOREPLY)&

&

(icmp->

icmp_id==pid))

{tvsend=(structtimeval*)icmp->

tv_sub(&

tvrecv,tvsend);

/*接收和发送的时间差*/

rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;

/*以毫秒为单位计算rtt*/

/*显示相关信息*/

%dbytefrom%s:

icmp_seq=%uttl=%drtt=%.3fms\n"

len,

inet_ntoa(from.sin_addr),

icmp_seq,

ip->

ip_ttl,

rtt);

elsereturn-1;

main(intargc,char*argv[])

{structhostent*host;

structprotoent*protocol;

unsignedlonginaddr=0l;

intwaittime=MAX_WAIT_TIME;

intsize=50*1024;

if(argc<

2)

usage:

%shostname/IPaddress\n"

argv[0]);

if((protocol=getprotobyname("

icmp"

))==NULL)

getprotobyname"

/*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/

if((sockfd=socket(AF_INET,SOCK_RAW,protocol->

p_proto))<

socketerror"

/*回收root权限,设置当前用户权限*/

setuid(getuid());

/*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的

的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/

setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&

size,sizeof(size));

bzero(&

dest_addr,sizeof(dest_addr));

dest_addr.sin_family=AF_INET;

/*判断是主机名还是ip地址*/

if(inaddr=inet_addr(argv[1])==INADDR_NONE)

{if((host=gethos

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 简洁抽象

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1