C语言模拟Ping

作者在 2007-12-23 01:41:14 发布以下内容

最近在学习TCP/IP协议。在以前套接字基础上又学习了原始套接字的使用。并喜欢上了这个东西-因为它功能太强大了。

下面是我用原始套接字实现的Ping.exe命令
这是运行后的效果(嘿嘿,还真相Ping程序,哈哈)

//Ping模拟  By RedIce
//E-mail:redice@see.xidian.edu.cn
//http://redice.1.suhai.com.cn
#include <winsock2.h> //Winsock API头文件
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib") //Winsock API连接库文件
/*IP头 结构*/
//BYTE<=>unsigned char,自定义类型
//USHORT<=>unsigned short,自定义类型
//UINT<=>unsigned int,自定义类型
//ULONG<=>unsigned long,自定义类型
typedef struct iphdr{
 BYTE  h_len:4;    //首部长度指的是IP层头部占32 bit字的数目
       //(也就是IP层头部包含多少个4字节,实际字节数4*hlen),
 BYTE  version:4; //IP版本号
 BYTE  tos;      //服务类型TOS
 USHORT total_len; //IP包总长度 
 USHORT ident;     //标识
 USHORT frag_and_flags;  //标志位
 BYTE ttl;      //生存时间
 BYTE proto;    //协议
 USHORT checksum; //IP首部校验和
 UINT  sourceIP; //源IP地址(32位)
 UINT  destIP;  //目的IP地址(32位)
}IpHeader;
/*ICMP头 结构*/
typedef struct _ihdr{
 BYTE i_type;   //类型 发出的ICMP为8(ICMP_ECHO_REQUEST),接受到的ICMP为0
 BYTE i_code;   //代码
 USHORT i_cksum; //ICMP包校验和
 USHORT i_id;    //识别号(一般用进程号作为标识号)
 USHORT i_seq;   //报文序列号(一般设置为0)
 ULONG timestamp;//时间戳
}IcmpHeader;

USHORT checksum(USHORT *,int); //函数声明:计算ICMP包校验和
void usage();//函数声明:使用帮助
void main(int argc,char *argv[])
{
 char *ICMP_DEST_IP;//目标主机IP
 WSADATA wsaData;
 struct sockaddr_in dest,from;//地址结构
 int datasize; //ICMP报文大小
 int ret;//API函数 返回值
 int i;//循环计数器
 int attachsize=32;//ICMP数据包附加字节数,本程序默认为32字节
 int n=4;//发送数据包个数,本程序默认发送4个ICMP数据包
 int timeout;//延迟
 DWORD packetrecv=0;//收到的数据包
 DWORD mintime=0;//用时最短时间
 DWORD maxtime=0;//用时最长时间
 DWORD averagetime=0;//平均用时
 DWORD lostpercent=0;//丢包率
 DWORD start;//发送ICMP包起始时间
 char *icmp_data;//ICMP包
 char *recvbuf;//ICMP应答接收缓冲区
 int fromlen=sizeof(from);//地址结构体的大小
 SOCKET sockRaw;//原始套接字
 char *attachdata;//ICMP包附加数据
 PHOSTENT hostinfo;//主机信息(域名->IP)
 //读取命令行参数
 if(1==argc)//如果仅有一个默认的命令行参数则显示程序说明
         //默认的第一个命令行参数为本程序的路径
 { 
  usage();
  return;//退出程序
 }
 else //如果有多个命令行参数
 {
  for(i=1;i<=argc-1;i++)
  {
   if(strstr(argv[i],"-n"))//"-n" 指定发送ICMP数据包个数
   {
    n=atoi(argv[i+1]);
    i++;
   }
   if(strstr(argv[i],"-l"))//"-l" 指定每个包附加数据的大小
   {
    attachsize=atoi(argv[i+1]);
    i++;
   }
   if(strstr(argv[i],"-t"))//"-t" 死亡之Ping
   {
    n=999999;
   }
   if(strstr(argv[i],"?"))
   {
    usage();
    return;//退出程序
   }
  }
  ICMP_DEST_IP=argv[argc-1];
 }

 //初始化Socket
 ret=WSAStartup(MAKEWORD(2,2), //Socket版本号
       &wsaData //指向WSADATA数据结构的指针
      );
 if(ret!=0)//WSAStartup调用成功返回0
 {
  printf("初始化Socket出错!\n");
  return;//退出程序 
 }
 //域名解析
 hostinfo=gethostbyname(ICMP_DEST_IP);
 if(NULL==hostinfo)
 {
  printf("无法解析主机%s的IP地址!",ICMP_DEST_IP);
  WSACleanup(); //中止Windows Sockets DLL的使用,释放资源
  return;
 }
 else
 {
  ICMP_DEST_IP=inet_ntoa(*(struct in_addr*)*hostinfo->h_addr_list);
 }

 //创建原始套接字
 sockRaw=socket(AF_INET,//协议族(AF_INET: TCP_IP)
       SOCK_RAW,//套接字类型(原始套接字)
       IPPROTO_ICMP//协议类型(ICMP协议)
       );
 if(INVALID_SOCKET==sockRaw)//socket调用失败返回INVALID_SOCKET,反之返回套接字句柄
 {
  printf("创建原始套接字出错!\n");
  WSACleanup(); //中止Windows Sockets DLL的使用,释放资源
  return;//退出程序
 }
 
 //设置目的IP
    memset(&dest,0,sizeof(dest));
 dest.sin_addr.S_un.S_addr=inet_addr(ICMP_DEST_IP);
 dest.sin_family=AF_INET;
 
 attachdata=(char*)malloc(attachsize);
 memset(attachdata,0,attachsize);//ICMP包数据部分 填充attachsize字节0
 datasize=sizeof(IcmpHeader)+attachsize;//ICMP数据包总大小(头+体)
 icmp_data=(char*)malloc(datasize);//根据上面计算的结果为ICMP包数据分配内存
 recvbuf=(char*)malloc(200);//
 memset(icmp_data,0,datasize);
 //ICMP数据包封装
 ((IcmpHeader *)icmp_data)->i_type=8;
 ((IcmpHeader *)icmp_data)->i_code=0;
 ((IcmpHeader *)icmp_data)->i_id=(USHORT)GetCurrentProcessId();
 ((IcmpHeader *)icmp_data)->timestamp=GetTickCount();
 ((IcmpHeader *)icmp_data)->i_seq=0;
 memcpy(icmp_data+sizeof(IcmpHeader),attachdata,attachsize);//ICMP包数据部分 填充32字节0
 ((IcmpHeader *)icmp_data)->i_cksum=0;
 //计算ICMP包校验和
 ((IcmpHeader *)icmp_data)->i_cksum=checksum((USHORT *)icmp_data,datasize);
 timeout=1000;
 //设置发送延迟
 setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(timeout));
 timeout=2000;
 //设置接受延迟
 setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(timeout));
 printf("Pinging %s with %d bytes of data:\n\n",ICMP_DEST_IP,attachsize);
 for(i=1;i<=n;i++)
 { start=GetTickCount();
  //发送第i个ICMP数据包
  ret=sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr *) &dest,sizeof(dest));
  if(SOCKET_ERROR==ret)
  {
   printf("发送ICMP数据包%d出错!\n",i);
   continue;
  }
  //等待接收返回的ICMP数据包
  while(1)
  {
   if((GetTickCount()-start)>=1000)
   {
    printf("Request timed out.\n");
    break;
   }
   memset(recvbuf,0,200);
   ret=recvfrom(sockRaw,recvbuf,200,0,(struct sockaddr *) &from,&fromlen);
   if(SOCKET_ERROR==ret)
   {
    printf("接收数据包%d出错!\n",i);
    break;
   }
   else
   {
    //显示返回ICMP包的信息
    unsigned short iphdrlen;//IP头长度
    IcmpHeader *icmphdr;
    DWORD timeuse;//数据包发送到接收用时
    int attachlen;//返回的ICMP数据包中数据段的大小
    timeuse=(GetTickCount()-start);
    if(0==packetrecv)
    {
     mintime=timeuse;
     maxtime=timeuse;
    }
    if(timeuse>maxtime) maxtime=timeuse;
    if(timeuse<mintime) mintime=timeuse;
    averagetime=averagetime+timeuse;
    iphdrlen=((IpHeader *)recvbuf)->h_len *4;
    icmphdr=(IpHeader *)(recvbuf+iphdrlen);
    attachlen=ret-iphdrlen-sizeof(IcmpHeader);
    printf("Reply from %s: bytes=%d time=%d ms TTL=%d\n",ICMP_DEST_IP,attachlen,timeuse,((IpHeader *)recvbuf)->ttl);
    packetrecv++;
    break;
   }
  
  }
 }
 //显示统计信息
 lostpercent=100*((float)(n-packetrecv)/(float)n);//计算丢包率
 averagetime=(float)averagetime/(float)packetrecv;//计算平均用时
 printf("\nPing statistics for %s:\n",ICMP_DEST_IP);
 printf("    Packets: Sent = %d, Received = %d, Lost = %d (%d %s loss),\n",n,packetrecv,lostpercent,lostpercent,"%");
 printf("Approximate round trip times in milli-seconds:\n");
 printf("    Minimum = %dms, Maximum = %dms, Average = %dms",maxtime,mintime,averagetime);
 free(recvbuf);
 free(icmp_data);
 closesocket(sockRaw);
 WSACleanup();
}
//计算ICMP数据包校验和
USHORT checksum(USHORT *buffer,int size)
{
 unsigned long cksum=0;
 while(size>1)
 {
  cksum+=*buffer++;
  size-=sizeof(USHORT);
 }
 if(size)
 {
  cksum+=*(UCHAR *)buffer;
 }
 cksum=(cksum>>16)+(cksum & 0xffff);
 cksum+=(cksum>>16);
 return (USHORT) (~cksum);
}
//使用说明
void usage()
{
 printf("Ping程序模拟\n\n");
 printf("By RedIce\n");
 printf("E-mail:redice@see.xidian.edu.cn\n");
 printf("http://redice.1.suhai.com.cn\n\n");
 printf("Usage: checknet.exe [-n count] [-l size] target_name \n\n");
 printf("Options:\n");
 printf(" -n count       发送ICMP数据包的个数,默认为4个.\n");
    printf(" -l size        每个ICMP包附加的数据字节数,默认为32字节.\n\n");
}

TCP/IP | 阅读 4702 次
文章评论,共2条
燃燒
2007-12-30 16:27
1
不错 顶一下 !
redice(作者)
2008-01-09 16:56
2
谢谢大家的关注 我会继续提高 继续共享,交流
游客请输入验证码
浏览585882次