Linux网络编程之原始套接字ping协议实现_第1页
Linux网络编程之原始套接字ping协议实现_第2页
Linux网络编程之原始套接字ping协议实现_第3页
Linux网络编程之原始套接字ping协议实现_第4页
Linux网络编程之原始套接字ping协议实现_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1.概述PING协议是用来检验本地主机与远程主机是否连接,发送的是ICMP ECHO_REQUEST包。普通的套接字是基于TCP或者是UDP的,无法发送ICMP包,所以必须用原始套接字来实现。PING协议的客户端类型值为8,代码值为0,表示请求。而PING协议的响应端类型值为0,代码值也为0,表示应答. 以太网数据部分的最小值为46字节,而IP首部占20个字节,ICMP的首部占8个字节,所以PING的数据部分至少为4字节。2.实现细节主机端:(1)创建原始套接字 socket(AF_INET,SOCK_RAW,htons(proto),能够直接得到IP包(2)填写ICMP首部和数据部分,即ic

2、mp_type(8),icmp_code(0)和icmp_data部分(3)封装后发送ICMP请求包响应端:(1)创建原始套接字socket(AF_INET,SOCK_RAW,htons(proto)(2)填写ICMP首部和数据部分,即icmp_type(0),icmp_code(0),icmp_data(3)发送ICMP响应包主机端收到ICMP响应包之后,即原始的IP包,将收到包的时间减去包的发送时间就可以得到响应时延。3. PING协议的实现例子#include <sys/socket.h>#include <netinet/in.h>#include <ne

3、tinet/ip.h>#include <netinet/ip_icmp.h>#include <unistd.h>#include <signal.h>#include <arpa/inet.h>#include <errno.h>#include <sys/time.h>#include <string.h>#include <netdb.h>#include <pthread.h>#include <stdlib.h>#include <stdio.h>

4、;/*通过原始套接字发送ICMP回显请求报文来实现ping协议ICMP回显报文的结构struct icmp  u_int8_t icmp_type;/消息类型  u_int8_t icmp_code;/消息类型的子码  u_int16_t icmp_cksum;/校验和  union   struct ih_idseq/显示数据报     u_int16_t icd_id;/数据报所在进程的ID     u_int16_6 icd_seq;/数据报序号ih

5、_idseq;icmp_hun;#define icmp_id icmp_hun.ih_idseq.icd_id#define icmp_seq icmp_hun.ih_idseq.icd_sequnion u_int8_t id_datai;/数据 icmp_dun;#define icmp_data icmp_dun.id_data*/typedef struct pingm_packet    struct timeval tv_begin;/发送的时间    struct timeval tv_end;/接

6、收到响应包的时间    short seq;/序号值    int flag;/1表示已经发送但没有接收到回应包,0表示接收到响应包pingm_packet;/保存已经发送包的状态值static pingm_packet pingpacket128;/定义一个包数组static pingm_packet *icmp_findpacket(int seq);static unsigned short icmp_cksum(unsigned char*data,int len);static struct timeval icmp_tvs

7、ub(struct timeval end,struct timeval begin);static void icmp_statistics(void);static void icmp_pack(struct icmp*icmph,int seq,struct timeval *tv,int length);static int icmp_unpack(char*buf,int len);static void* icmp_recv(void*argv);static void* icmp_send(void*argv);static void icmp_sigint(int signo)

8、;static void icmp_usage();#define K 1024#define BUFFERSIZE 512static unsigned char send_buffBUFFERSIZE;/定义发送缓冲区的大小static unsigned char recv_buff2*K;/定义接收缓冲区的大小,为防止接收端溢出,接收缓冲区稍微大一些static struct sockaddr_in dest;/目的地址static int rawsock=0;/原始套接字描述符static pid_t pid=0;/进程idstatic int alive=0;/是否接收到退出信号st

9、atic short packet_send=0;/已经发送的数据包数目static short packet_recv=0;/已经接收的数据报数目static char dest_str80;/目的主机字符串static struct timeval tv_begin,tv_end,tv_interval;/本程序开始发送,结束时间和时间间隔static void icmp_usage()printf("ping aaa.bbb.ccc.dddn");/计算ICMP首部校验和static unsigned short icmp_cksum(unsigned char* d

10、ata,int len) int sum=0; int odd=len&0x01;unsigned short *value=(unsigned short*)data;while(len&0xfffe)  sum+=*(unsigned short*)data;  data+=2;  len-=2;if(odd)  unsigned short tmp=(*data)<<8)&0xff00;  sum+=tmp;sum=(sum>>16)+(sum&0xffff);s

11、um+=(sum>>16);return sum;/设置ICMP报头static void icmp_pack(struct icmp *icmph,int seq,struct timeval*tv,int length)unsigned char i=0;icmph->icmp_type=ICMP_ECHO;/ICMP回显请求icmph->icmp_code=0;/code为0icmph->icmp_cksum=0;/cksum值icmph->icmp_seq=htons(seq);/数据报的序列号icmph->icmp_id=htons(pid&

12、amp;0xffff);/数据报的ID for(i=0;i<length;i+)   icmph->icmp_datai=htons(i);/注意主机字节序转换成网络字节序 /发送的数据 /计算校验和icmph->icmp_cksum=icmp_cksum(unsigned char*)icmph,length+8);/计算时间差函数static struct timeval icmp_tvsub(struct timeval end,struct timeval begin)  struct timeval tv;

13、  tv.tv_sec=end.tv_sec-begin.tv_sec;  tv.tv_usec=end.tv_usec-begin.tv_usec;  if(tv.tv_usec<0)     tv.tv_sec-;     tv.tv_usec+=1000000;   return tv;/发送报文static void *icmp_send(void *argv)   struct timeval tv;   tv

14、.tv_usec=0;   tv.tv_sec=1;/每隔一秒发送报文   gettimeofday(&tv_begin,NULL);/保存程序开始发送数据的时间   while(alive)      memset(send_buff,0,sizeof(send_buff);    int size=0;    struct timeval tv;    gettimeofday(&tv,

15、NULL);/当前包发送的时间    icmp_pack(struct icmp*)send_buff,packet_send,&tv,203);/packet_send为发送包的序号,发送的数据长度为64个字节,填充ICMP首部信息    size=sendto(rawsock,send_buff,203+8,0,(struct sockaddr*)&dest,sizeof(dest);/dest为ICMP包发送的目的地址    if(size<0)  

16、0;  perror("sendto error");     continue;     else  /在发送包的状态数组找一空闲位置记录发送状态信息  pingm_packet *packet=icmp_findpacket(-1);  if(packet)     packet->seq=packet_send;     packet->flag=1; 

17、60;   gettimeofday(&packet->tv_begin,NULL);     packet_send+;/发送序号1    sleep(1);/寻找一个空闲位置,seq=-1表示空闲位置static pingm_packet* icmp_findpacket(int seq)   int i=0;   pingm_packet* found=NULL;  if(seq=-1)    for(i=

18、0;i<128;i+)     if(pingpacketi.flag=0)      found=&pingpacketi;      break;       else if(seq>=0)/查找对应seq的数据包    for(i=0;i<128;i+)        if(p

19、ingpacketi.seq=seq)              found=&pingpacketi;        break;        return found;/获得ICMP接收报文,buf存放的除去了以太网部分的IP数据报文,len为数据长度,ip_hl标识IP头部长度以4字节为单位,获得ICMP数据报后判断是否为ICMP_

20、ECHOREPLY并检查是否为本进程的IDstatic int icmp_unpack(char*buf,int len)  int i,iphdrlen;  struct ip *ip=NULL;  struct icmp *icmp=NULL;  int rtt;/计算往返时延  ip=(struct ip*)buf;  iphdrlen=ip->ip_hl*4;/IP头部长度  icmp=(struct icmp*)(buf+iphdrlen);/ICMP报文的地址  len-=iphdrlen;/IC

21、MP报文的长度,ICMP报文至少8个字节  if(len<8)    printf("ICMP packets's length is less than 8n ");    return -1; /判断ICMP报文的类型是否为ICMPECHOREPLY并且为本进程的PIDif(icmp->icmp_type=ICMP_ECHOREPLY)&&(icmp->icmp_id=pid)  struct timeval tv_interval,tv_

22、recv,tv_send; /在发送数组中查找已经发送的包pingm_packet*packet=icmp_findpacket(ntohs(icmp->icmp_seq);/网络字节序转换成主机字节序if(packet=NULL) return -1; packet->flag=0;/表示已经响应了/本包的发送时间tv_send=packet->tv_begin;/读取收到响应包的时间 gettimeofday(&tv_recv,NULL); tv_interval=icmp_tvsub(tv_recv,tv_sen

23、d);/计算往返时延,即RTTrtt=tv_interval.tv_sec*1000+tv_interval.tv_usec/1000;/打印ICMP段长度,源IP,包的序列号,TTL,时间差printf("%d byte from %s:icmp_seq=%u ttl=%d rtt=%d msn",len,inet_ntoa(ip->ip_src),icmp->icmp_seq,ip->ip_ttl,rtt);packet_recv+;/接收包的数量加1elsereturn -1;/接收报文static void *icmp_recv(void*argv

24、) struct timeval tv;tv.tv_usec=200;/轮循时间tv.tv_sec=0;fd_set readfd;while(alive)  int ret=0;  tv.tv_usec=200;/轮循时间  tv.tv_sec=0;  FD_ZERO(&readfd);  FD_SET(rawsock,&readfd);  ret=select(rawsock+1,&readfd,NULL,NULL,&tv);  int fromlen=0;  stru

25、ct sockaddr from;  switch(ret)case -1:/发生错误break;case 0:/超时 /printf("timeoutn");break;default:/收到数据包 fromlen=sizeof(from); int size=recvfrom(rawsock,recv_buff,sizeof(recv_buff),0,(struct sockaddr*)&from,&fromlen);/利用原始套接字,原始套接字与IP层网络协议栈核心打交道 if(errno=EINTR)

26、   perror("recvfrom error"); /解包,得到RTT ret=icmp_unpack(recv_buff,size); if(ret=-1)   continue; break;/统计数据结果,成功发送的报文数量,成功接收的报文数量,丢失报文百分比和程序总共运行时间static void icmp_statistics(void)  long time=(tv_interval.tv_sec*1000)+(tv_interval.tv_usec/1000);&#

27、160; printf("- %s ping statistics -n",dest_str);/目的IP  printf("%d packets transmitted, %d recevied, %d%c packet loss, time %d msn",packet_send,packet_recv,(packet_send-packet_recv)*100/packet_send,'%',time);/信号处理函数static void icmp_sigint(int signo)   alive=

28、0;/alive=0程序将会终止   gettimeofday(&tv_end,NULL);/程序结束时间   tv_interval=icmp_tvsub(tv_end,tv_begin);/计算程序一共运行了多长时间  return;/主函数实现int main(int argc,char*argv)  struct hostent *host=NULL;  struct protoent*protocol=NULL;  char protoname="icmp"  uns

29、igned long inaddr=1;  int size=128*K;  int ret; if(argc<2)   icmp_usage();   return -1; /获取协议类型ICMP,协议类型的值作为设置原始套接字的第3个参数,type类型下的具体协议值不止一个,当type为SOCK_RAWprotocol=getprotobyname(protoname);if(protocol=NULL)  perror("getprotobyname()"); 

30、return -1;/复制目的地址memcpy(dest_str,argv1,strlen(argv1)+1);memset(pingpacket,0,sizeof(pingm_packet)*128);/pingpacket数组初始化/建立原始套接字rawsock=socket(AF_INET,SOCK_RAW,protocol->p_proto);if(rawsock<0)  perror("raw sock error");  return -1;/得到程序的pidpid=getuid();/增大接收端缓冲区防止接收的包被覆盖ret=s

31、etsockopt(rawsock,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size);if(ret=-1)   perror("SO_RCVBUF ERROR");   return -1;/输入的目的IPinaddr=inet_addr(argv1);/转换成二进制IPbzero(&dest,sizeof(dest);dest.sin_family=AF_INET;/设置地址族if(inaddr=INADDR_NONE)  /输入的是DNS   

32、host=gethostbyname(argv1);    if(host=NULL)     perror("gethostbyname");    return -1;   memcpy(char*)&dest.sin_addr,host->h_addr,host->h_length);else memcpy(char*)&dest.sin_addr,&inaddr,sizeof(inaddr);inadd

33、r=dest.sin_addr.s_addr;/由于是ICMP不涉及到端口绑定printf("PING %s (%d.%d.%d.%d) 56(84)bytes of data.n",dest_str,(inaddr&0x000000FF)>>0,(inaddr&0x0000FF00)>>8,(inaddr&0x00FF0000)>>16,(inaddr&0xFF000000)>>24);signal(SIGINT,icmp_sigint);alive=1;/定义两个线程,分别用于发送数据与接收

34、数据pthread_t send_id,recv_id;int err=0;err=pthread_create(&send_id,NULL,icmp_send,NULL);if(err<0)  return -1;err=pthread_create(&recv_id,NULL,icmp_recv,NULL);if(err<0)  return -1;pthread_join(send_id,NULL);/等待子线程结束sendpthread_join(recv_id,NULL);/等待子线程的结束recvclose(rawsock);icmp_statistics();return 0;运行结果:PING 222.27.253.1 (222.27.253.1) 56(84)bytes of data.60 byte from 222.27.253.1:icmp_seq=0 ttl=255 rtt=5 ms60 byte from 222.27.253.1:icmp_seq=1 ttl=255 rtt=12

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论