• ping程序的实现


    构造icmp包,发送给自己在同一网段的主机,使用select函数,非阻塞方式接收回包。还包括反码算术求和求首部校验和的函数。

    转载请注明出处。

    可能的情况

    1、在线

      目的主机直接回复icmp包。

    2、终点不可达(发送不到目的主机)

      接收到路由器或本机的icmp的终点不可达回包。

    3、接受不到回包(能发送到目的主机)

      能发送到目的主机,但是被目的主机的防火墙拦截了,不做回复,所以收不到回包。

    代码绝大部分是老师上课的内容,自己整理了一遍,加上了一些注释:

      1 // SetAvailableIP.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #pragma pack(4)
      5 /*
      6 *编译器中提供了#pragma pack(n)来设定变量以n字节对齐方式。
      7 *n字节对齐就是说变量存放的起始地址的偏移量有两种情况,
      8 *第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,
      9 *第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
     10 *结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,
     11 *那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
     12 */
     13 
     14 
     15 #define WIN32_LEAN_AND_MEAN
     16 #include "stdafx.h"
     17 #include <stdlib.h>
     18 #include <WINSOCK2.H>//与#include <winsock2.h>相同
     19 #pragma comment(lib,"ws2_32.lib")
     20 
     21 
     22 #define ICMP_ECHO 8
     23 #define ICMP_ECHOREPLY 0
     24 
     25 #define ICMP_MIN 8//// minimum 8 byte icmp packet (just header)
     26 #define uchar unsigned char
     27 #define uint unsigned int
     28 
     29 
     30 /* ip header*/
     31 typedef struct iphdr{
     32     uchar h_len:4;//位域,占四个二进制位 length of header
     33     uchar version:4;//Version of ip   因为位序是小端所以颠倒位置
     34     uchar tos;        //type of service
     35     uchar total_len;//total length of packet
     36     uchar ident;    //unique idetifier  超出mtu,需要分片时的组号标识
     37     uchar frag_and_flags;//标志3位   片偏移
     38     uchar ttl;            //time to live
     39     uchar proto;        //protocol (TCPUDP etc)
     40     uchar checksum;        //IP checksum
     41     uint sourceIP;        
     42     uint destIP;
     43 }IpHeader;
     44 
     45 
     46 // ICMP header
     47 //
     48 typedef struct _ihdr {
     49   BYTE i_type;
     50   BYTE i_code; /* type sub code */
     51   USHORT i_cksum;
     52   USHORT i_id;
     53   USHORT i_seq;
     54   /* This is not the std header, but we reserve space for time */
     55   ULONG timestamp;
     56 }IcmpHeader;
     57 
     58 
     59 #define STATUS_FAILED 0xFFFF
     60 #define DEF_PACKET_SIZE 32
     61 #define MAX_PACKET 1024
     62 
     63 #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
     64 #define xfree(p)   HeapFree (GetProcessHeap(),0,(p))
     65 
     66 void fill_icmp_data(char *, int);
     67 USHORT checksum(USHORT *, int);
     68 void decode_resp(char *,int ,struct sockaddr_in *);
     69 bool ping(IN_ADDR ip);
     70 
     71 void Usage(char *progname){
     72   
     73   fprintf(stderr,"Usage:
    ");
     74   fprintf(stderr,"%s <host> [data_size]
    ",progname);
     75   fprintf(stderr,"datasize can be up to 1Kb
    ");
     76   ExitProcess(STATUS_FAILED);
     77 }
     78 
     79 
     80 
     81 int _tmain(int argc, _TCHAR* argv[])
     82 {
     83     IN_ADDR ipStart,ipEnd,ip,netMask;
     84     unsigned int iStart,iEnd,i;
     85 
     86 
     87     WSADATA wsaData;
     88     WSAStartup(MAKEWORD(2,2),&wsaData);//当一个应用程序调用WSAStartup函数时,
     89     //操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。
     90     //以后应用程序就可以调用所请求的Socket库中的其它Socket函数了
     91 
     92 
     93     ip.s_addr = inet_addr("192.168.26.155");//点分十进制转换成网络字节顺序
     94     netMask.s_addr = inet_addr("255.255.255.128");
     95     ipStart.s_addr = ip.s_addr & netMask.s_addr;//两个unlong,ip与掩码与 求出ip最小值
     96     ipEnd.s_addr = (ipStart.s_addr & netMask.s_addr) | ~netMask.s_addr;
     97 
     98     iStart = ntohl(ipStart.s_addr);//网络自己顺序转换成主机字节顺序
     99     iEnd = ntohl(ipEnd.s_addr);
    100     
    101     for(i = iStart+2;i<iEnd;i++)//iStart不可用,iStart+1一般做默认网关地址,iStart+2开始
    102     {
    103         ip.s_addr = htonl(i);
    104         printf("%s
    ",inet_ntoa(ip));//in_addr(点分十进制)转换成字符串
    105         ping(ip);
    106     }
    107     WSACleanup();
    108     return 0;
    109 }
    110 
    111 
    112 bool ping(IN_ADDR ip)//ping程序
    113 {
    114     SOCKET sockRaw;
    115     struct sockaddr_in dest,from;
    116     int bread,datasize;
    117     int fromlen = sizeof(from);
    118     int timeout = 1000;
    119     char *dest_ip;
    120     char *icmp_data;
    121     char *recvbuf;
    122     unsigned int addr = 0;
    123     USHORT seq_no = 0;
    124 
    125     struct timeval tv;
    126     tv.tv_sec =  3;
    127     tv.tv_usec = 0;
    128 
    129     
    130     sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,0);
    131 
    132     if(sockRaw == INVALID_SOCKET)
    133     {
    134         fprintf(stderr,"WSASocket() failed:%d
    ",WSAGetLastError());
    135         ExitProcess(STATUS_FAILED);
    136     }
    137 
    138     bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));//设置1秒超时时间
    139 
    140     if(bread == SOCKET_ERROR) {
    141           fprintf(stderr,"failed to set recv timeout: %d
    ",WSAGetLastError());
    142         ExitProcess(STATUS_FAILED);
    143     }
    144 
    145     timeout = 1000;
    146     bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,
    147                       sizeof(timeout));
    148     if(bread == SOCKET_ERROR) {
    149           fprintf(stderr,"failed to set send timeout: %d
    ",WSAGetLastError());
    150         ExitProcess(STATUS_FAILED);
    151     }
    152 
    153     memset(&dest,0,sizeof(dest));//初始化
    154 
    155     dest.sin_addr = ip;
    156     dest.sin_family = AF_INET;
    157     dest_ip = inet_ntoa(dest.sin_addr);
    158 
    159     datasize = DEF_PACKET_SIZE;
    160     datasize += sizeof(IcmpHeader);
    161 
    162     icmp_data = (char*)xmalloc(MAX_PACKET);
    163     recvbuf = (char*)xmalloc(MAX_PACKET);
    164 
    165 
    166     if (!icmp_data) {
    167         fprintf(stderr,"HeapAlloc failed %d
    ",GetLastError());
    168         ExitProcess(STATUS_FAILED);
    169     }
    170 
    171 
    172     memset(icmp_data,0,MAX_PACKET);
    173     fill_icmp_data(icmp_data,datasize);
    174 
    175 
    176     //while(1) {
    177         int bwrote;
    178 
    179         ((IcmpHeader*)icmp_data)->i_cksum = 0;
    180         ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
    181 
    182         ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
    183         ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, 
    184             datasize);
    185 
    186         bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,
    187             sizeof(dest));
    188 
    189         if (bwrote == SOCKET_ERROR){
    190             if (WSAGetLastError() == WSAETIMEDOUT) {
    191                 printf("timed out
    ");
    192                 //continue;
    193             }
    194             fprintf(stderr,"sendto failed: %d
    ",WSAGetLastError());
    195             ExitProcess(STATUS_FAILED);
    196         }
    197         if (bwrote < datasize ) {
    198             fprintf(stdout,"Wrote %d bytes
    ",bwrote);
    199         }
    200 
    201 
    202 
    203         fd_set socketset;
    204         memset(&socketset, 0, sizeof(socketset));
    205         socketset.fd_count = 1;
    206         socketset.fd_array[0] = sockRaw;
    207         bread = select(0, &socketset, NULL, NULL, &tv);//非阻塞方式接收数据,接收不到显示超时继续ping后面的IP
    208         if (bread == 0){
    209             //if (WSAGetLastError() == WSAETIMEDOUT) {
    210             printf("recv timed out
    ");
    211             //continue;
    212             //}
    213             //fprintf(stderr,"recvfrom failed: %d
    ",WSAGetLastError());
    214             //ExitProcess(STATUS_FAILED);
    215         }
    216         else{
    217             bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,
    218                 &fromlen);
    219             decode_resp(recvbuf,bread,&from);
    220         }
    221         //Sleep(1000);
    222 
    223     //}
    224 
    225     xfree(icmp_data);
    226     xfree(recvbuf);
    227 
    228 }
    229 
    230 
    231 USHORT checksum(USHORT *buffer, int size) {//校验和
    232 
    233   unsigned long cksum=0;
    234 
    235   while(size >1) {
    236     cksum+=*buffer++;
    237     size -=sizeof(USHORT);
    238   }
    239   
    240   if(size ) {
    241     cksum += *(UCHAR*)buffer;
    242   }
    243 
    244   cksum = (cksum >> 16) + (cksum & 0xffff);//反码算术运算求和  
    245   cksum += (cksum >>16);//最多进一位,右移加上进位,不可能再次产生进位  比如99+99 =198  98+1=99 不会继续产生进位  
    246   return (USHORT)(~cksum);
    247 }
    248 
    249 /* 
    250     Helper function to fill in various stuff in our ICMP request.
    251 */
    252 void fill_icmp_data(char * icmp_data, int datasize){
    253 
    254   IcmpHeader *icmp_hdr;
    255   char *datapart;
    256 
    257   icmp_hdr = (IcmpHeader*)icmp_data;
    258 
    259   icmp_hdr->i_type = ICMP_ECHO;
    260   icmp_hdr->i_code = 0;
    261   icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
    262   icmp_hdr->i_cksum = 0;
    263   icmp_hdr->i_seq = 0;
    264   
    265   datapart = icmp_data + sizeof(IcmpHeader);//截取数据部分
    266   //
    267   // Place some junk in the buffer.
    268   //
    269   memset(datapart,'E', datasize - sizeof(IcmpHeader));//填充数据
    270 }
    271 
    272 /* 
    273     The response is an IP packet. We must decode the IP header to locate 
    274     the ICMP data 
    275 */
    276 void decode_resp(char *buf, int bytes,struct sockaddr_in *from) {
    277 
    278     IpHeader *iphdr;
    279     IcmpHeader *icmphdr;
    280     unsigned short iphdrlen;
    281 
    282     iphdr = (IpHeader *)buf;
    283 
    284     iphdrlen = iphdr->h_len * 4; // number of 32-bit words *4 = bytes
    285 
    286     if (bytes  < iphdrlen + ICMP_MIN) {
    287         printf("Too few bytes from %s
    ",inet_ntoa(from->sin_addr));
    288     }
    289 
    290     icmphdr = (IcmpHeader*)(buf + iphdrlen);
    291 
    292     if (icmphdr->i_type != ICMP_ECHOREPLY) {
    293         fprintf(stderr,"non-echo type %d recvd
    ",icmphdr->i_type);
    294         return;
    295     }
    296     if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {
    297         fprintf(stderr,"someone else's packet!
    ");
    298         return ;
    299     }
    300     printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));
    301     printf(" icmp_seq = %d. ",icmphdr->i_seq);
    302     printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);//GetTickCount()操作系统从启动经过的毫秒数
    303     printf("
    ");
    304         
    305 }
  • 相关阅读:
    物联网与边缘计算的融合
    在【自我认知】大学,你可能永远毕不了业
    Spring Security实现短信验证码登录
    线上课程
    【技术人成长】公众号
    大数据是阿猫阿狗都能玩的吗
    机器不能代替你思考
    如何缓解需求沟通中的鸡同鸭讲
    如何成为一个更渊博的技术人
    招聘季,聊聊那些古怪的候选人
  • 原文地址:https://www.cnblogs.com/tshua/p/5350221.html
Copyright © 2020-2023  润新知