Windows平台Ping示例源码分析(C/C++)

//-----------------------iphdr.h-----------------------//

//源码分析将忽略ipv6

//边界对齐至字节

pshpack1.h为官方头文件,不做赘述。

#include <pshpack1.h>

// 1 -- ipv4 头部

typedef struct ip_hdr

{

    unsigned char  ip_verlen;       // 前4位IP版本号(IPv4 或者IPv6)

                                    // 后4位头部长度(32位,4字节)(1.1)

    unsigned char  ip_tos;          // 前3位为优先级,后5位为服务类型(1.2)

    unsigned short ip_totallength;  // 16位包总长度包括头部和数据(字节)(1.3)

    unsigned short ip_id;           // 16位ID标识

    unsigned short ip_offset;       // 前3位为分段标识,后5位为分段偏移

    unsigned char  ip_ttl;          // 该包可经过的路由器数量上限

    unsigned char  ip_protocol;     // 协议类型(TCP,UDP,ICMP,IGMP等)

    unsigned short ip_checksum;     // ipv4 头部的校验和

    unsigned int   ip_srcaddr;      // ipv4 源地址

    unsigned int   ip_destaddr;     // ipv4 目的地址

} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
此为IPv4头部定义。IPv4头部一般为20字节大小除非使用到选项位。

(1.1) 由于最大头部的限制,ping所具有路由记录功能在如今的网络拓扑中无法使用(只能记录9个ipv4地址)。此功能由traceroute替代。
(1.2) 以上参照CCNA Study Guide的说法。在TCP/IP illustrated Volume I中前3位被忽略,后4位分别代表minimize delay, maximize throughput, maximize reliability, 和minimize monetray cost,最后1位被置0并忽略。
(1.3) 理论上ipv4可以达到的最大长度为65536字节,除了在超级通道(hyperchannel)中出现这种最大传输单元外,在普通网络中通常只允许 8192字节大小的包传输。TCP为流协议没有大小限制,UDP最大包为512字节。一台主机一次可接收的最大数据包为576字节。

 

// 2 -- ipv4 选项头部

typedef struct ipv4_option_hdr

{

    unsigned char   opt_code;          // ipv4 选项头类型

    unsigned char   opt_len;           // ipv4 选项头长度

    unsigned char   opt_ptr;           // ipv4 选项头指针

    unsigned long   opt_addr[9];       // ipv4 9个地址列表(2.1)

} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;

(2.1) 参照(1.1)
 

// 3 -- icmp 头部

typedef struct icmp_hdr

{

    unsigned char   icmp_type;            // icmp 类型

    unsigned char   icmp_code;            // ipv4 码

    unsigned short  icmp_checksum;        // icmp 头部及数据校验和

    unsigned short  icmp_id;              // icmp id标识(3.1)

    unsigned short  icmp_sequence;        // icmp 序列号,请求回应消息对

} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;

(3.1) id标识一般为发送icmp回显请求的进程号
 

// 4 -- udp 头部(此头部未在程序中用到)

typedef struct udp_hdr

{

    unsigned short src_portno;            // 源端口

    unsigned short dst_portno;            // 目的端口

    unsigned short udp_length;            // udp 包总长度(字节)

    unsigned short udp_checksum;          // udp 头部以及数据校验和(4.1)

} UDP_HDR, *PUDP_HDR;

(4.1) UDP,TCP校验和都包括头部和数据。IP校验和只涉及头部。UDP校验和可选,TCP为强制。

// 5 -- ipv4  路径记录宏

#define IP_RECORD_ROUTE     0x7            

// icmp 类型和码(5.1)

#define ICMPV4_ECHO_REQUEST_TYPE   8        // icmp  回显请求类型

#define ICMPV4_ECHO_REQUEST_CODE   0        // icmp  回显请求码

#define ICMPV4_ECHO_REPLY_TYPE     0        // icmp  回显回应类型

#define ICMPV4_ECHO_REPLY_CODE     0        // icmp  回显回应码

#define ICMPV4_MINIMUM_HEADER      8        // icmp  最小头部

(5.1) 参照TCP/IP Illustrated : Volume 1 Chapter 6 ICMP   ICMP 消息类型

 //-----------------------resolve.h---------------------//

// 恢复默认对齐方式

#include <poppack.h>

#ifndef _RESOLVE_H_

#define _RESOLVE_H_

// 在C++编译器中以C语言的方式编译

#ifdef _cplusplus

extern "C" {

#endif

 

int              PrintAddress(SOCKADDR *sa, int salen);

int              FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen);

int              ReverseLookup(SOCKADDR *sa, int salen, char *namebuf, int namebuflen);

struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto);

 

#ifdef _cplusplus

}

#endif

 

#endif

//-----------------------resolve.cpp---------------------//

// 6

#include <winsock2.h>              // socket 标准头文件

#include <ws2tcpip.h>              // TCP/IP实现相关(6.1)

#include <strsafe.h>               // 提供安全的字符串操作(6.2)

#include <stdio.h>

#include <stdlib.h>

 

#include "resolve.h"

(6.1) 此头文件提供 getnameinfo,getaddrinfo函数。

(6.2) StringCchPrintf, StringCchCopy 具有相对于printf 和strcpy函数更多的缓冲区安全机制。

// 7

int PrintAddress(SOCKADDR *sa, int salen)

{

    char    host[NI_MAXHOST],

            serv[NI_MAXSERV];

    int     hostlen = NI_MAXHOST,

            servlen = NI_MAXSERV,

            rc;

 

    rc = getnameinfo(               // 提供协议无关的名字解析(7.1)

            sa,

            salen,

            host,

            hostlen,

            serv,

            servlen,

            NI_NUMERICHOST | NI_NUMERICSERV

            );

    if (rc != 0)

    {

        fprintf(stderr, "%s: getnameinfo failed: %d/n", __FILE__, rc);

        return rc;

    }

    if (strcmp(serv, "0") != 0)

    {

        if (sa->sa_family == AF_INET)

            printf("[%s]:%s", host, serv);

        else

            printf("%s:%s", host, serv);

    }

    else

        printf("%s", host);

 

    return NO_ERROR;

}

(7.1) host 缺省返回为一个在网络上的完整域名。serv返回为一个端口服务名。NI_NUMBERICHOST | NI_NUMBERICSERV 意味着返回以数字形式表示的host(IP地址)和serv(端口号)。
此函数与ResolveAddress函数的区别为此函数将addrinfo结构中sockaddr翻译成为可读信息。

int FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen)

{

    char    host[NI_MAXHOST],

            serv[NI_MAXSERV];

    int     hostlen = NI_MAXHOST,

            servlen = NI_MAXSERV,

            rc;

    HRESULT hRet;

 

    rc = getnameinfo(

            sa,

            salen,

            host,

            hostlen,

            serv,

            servlen,

            NI_NUMERICHOST | NI_NUMERICSERV

            );

    if (rc != 0)

    {

        fprintf(stderr, "%s: getnameinfo failed: %d/n", __FILE__, rc);

        return rc;

    }

 

    if ( (strlen(host) + strlen(serv) + 1) > (unsigned)addrbuflen)

        return WSAEFAULT;

 

    addrbuf[0] = '/0';

 

    if (sa->sa_family == AF_INET)

    {

        if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "%s:%s", host, serv)))

        {

            fprintf(stderr,"%s StringCchPrintf failed: 0x%x/n",__FILE__,hRet);

            return (int)hRet;

        }

    }

    else if (sa->sa_family == AF_INET6)

    {

        if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "[%s]:%s", host, serv)))

        {

            fprintf(stderr,"%s StringCchPrintf failed: 0x%x/n",__FILE__,hRet);

            return (int)hRet;

        }

    }

    return NO_ERROR;

}

以上代码将解析出的IP地址和端口号以 X:X的形式存放入字符串。

struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto)

{

    struct addrinfo hints,

    *res = NULL;

    int             rc;

 

    memset(&hints, 0, sizeof(hints));

    hints.ai_flags  = ((addr) ? 0 : AI_PASSIVE);

    hints.ai_family = af;

    hints.ai_socktype = type;

    hints.ai_protocol = proto;

 

    rc = getaddrinfo(

            addr,

            port,

           &hints,

           &res

            );

    if (rc != 0)

    {

        fprintf(stderr, "Invalid address %s, getaddrinfo failed: %d/n", addr, rc);

        return NULL;

    }

    return res;

}

以上代码解析出域名将地址信息存入addrinfo结构。

int ReverseLookup(SOCKADDR *sa, int salen, char *buf, int buflen)

{

    char    host[NI_MAXHOST];

    int     hostlen=NI_MAXHOST,

            rc;

    HRESULT hRet;

   

    rc = getnameinfo(

            sa,

            salen,

            host,

            hostlen,

            NULL,

            0,

            0

            );

    if (rc != 0)

    {

        fprintf(stderr, "getnameinfo failed: %d/n", rc);

        return rc;

    }

 

    buf[0] = '/0';

    if(FAILED(hRet = StringCchCopy(buf, buflen, host)))

    {

        fprintf(stderr,"StringCchCopy failed: 0x%x/n",hRet);

        return (int)hRet;

    }

 

    return NO_ERROR;

}

DNS逆向查找(此函数未被调用过)。  此函数和PrintAddress 函数功能相近。

//-----------------------ping.cpp---------------------//

// 64位架构

#ifdef _IA64_

#pragma warning (disable: 4267)

#endif

// 此宏定义使windows.h剔除部分头文件,加快编译速度

#ifndef WIN32_LEAN_AND_MEAN

#define WIN32_LEAN_AND_MEAN

#endif

 

#include <winsock2.h>

#include <ws2tcpip.h>

#include <stdio.h>

#include <stdlib.h>

#include "resolve.h"

#include "iphdr.h"

 

#define DEFAULT_DATA_SIZE      32       // icmp 数据段大小

#define DEFAULT_SEND_COUNT     4        // ping 的次数

#define DEFAULT_RECV_TIMEOUT   6000     // 接收超时

#define DEFAULT_TTL            128      // 最大跳站术

#define MAX_RECV_BUF_LEN       0xFFFF   // 最大接收缓冲区大小

 

int   gAddressFamily=AF_UNSPEC,        

      gProtocol=IPPROTO_ICMP,          

      gTtl=DEFAULT_TTL;                

int   gDataSize=DEFAULT_DATA_SIZE;     

BOOL  bRecordRoute=FALSE;               //  是否记录路由

char *gDestination=NULL,               

      recvbuf[MAX_RECV_BUF_LEN];       

int   recvbuflen = MAX_RECV_BUF_LEN; 

//  ping 命令使用方法

void usage(char *progname)

{

    printf("usage: %s [options] <host> /n", progname);

    printf("        host        Remote machine to ping/n");

    printf("        options: /n");

    printf("            -a 4|6       Address family (default: AF_UNSPEC)/n");

    printf("            -i ttl       Time to live (default: 128) /n");

    printf("            -l bytes     Amount of data to send (default: 32) /n");

    printf("            -r           Record route (IPv4 only)/n");

 

    return;

}

//  初始话icmp头部

void InitIcmpHeader(char *buf, int datasize)

{

    ICMP_HDR   *icmp_hdr=NULL;

    char       *datapart=NULL;

    //  详见icmp头部定义

    icmp_hdr = (ICMP_HDR *)buf;

    icmp_hdr->icmp_type     = ICMPV4_ECHO_REQUEST_TYPE;  

    icmp_hdr->icmp_code     = ICMPV4_ECHO_REQUEST_CODE;

    icmp_hdr->icmp_id       = (USHORT)GetCurrentProcessId();  //进程号

    icmp_hdr->icmp_checksum = 0;   //  序列号未定义,校验和未计算

    icmp_hdr->icmp_sequence = 0;   //  序列号置空

 

    datapart = buf + sizeof(ICMP_HDR);   //  指针移至数据段头

    memset(datapart, 'E', datasize);     //  填充数据段

}

// 计算校验和

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);

}

// 传参解析

BOOL ValidateArgs(int argc, char **argv)

{

    int                i;

    BOOL               isValid = FALSE;

 

    for(i=1; i < argc ;i++)

    {

        if ((argv[i][0] == '-') || (argv[i][0] == '/'))

        {

            switch (tolower(argv[i][1]))

            {

                case 'a':       

                    if (i+1 >= argc)

                    {

                        usage(argv[0]);

                        goto CLEANUP;

                    }

                    if (argv[i+1][0] == '4')

                        gAddressFamily = AF_INET;

                    else if (argv[i+1][0] == '6')

                        gAddressFamily = AF_INET6;

                    else

                    {

                        usage(argv[0]);

                        goto CLEANUP;

                    }

 

                    i++;

                    break;

                case 'i':        // 设置最大跳值

                    if (i+1 >= argc)

                    {

                        usage(argv[0]);

                        goto CLEANUP;

                    }

 

                    gTtl = atoi(argv[++i]);

                    break;

                case 'l':        //  icmp数据段大小设置

                    if (i+1 >= argc)

                    {

                        usage(argv[0]);

                        goto CLEANUP;

                    }

 

                    gDataSize = atoi(argv[++i]);

                    break;

                case 'r':        //  记录路由选项

                    bRecordRoute = TRUE;

                    break;

                default:

                    usage(argv[0]);

                    goto CLEANUP;

            }

        }

        else

        {

            gDestination = argv[i];

        }

    }

 

    isValid = TRUE;

 

CLEANUP:   

    return isValid;

}

//  设置icmp序列号

void SetIcmpSequence(char *buf)

{

    ULONG    sequence=0;

 

    sequence = GetTickCount();

    if (gAddressFamily == AF_INET)

    {

        ICMP_HDR    *icmpv4=NULL;

 

        icmpv4 = (ICMP_HDR *)buf;

 

        icmpv4->icmp_sequence = (USHORT)sequence;

    }

    else if (gAddressFamily == AF_INET6)

    {

        ICMPV6_HDR          *icmpv6=NULL;

        ICMPV6_ECHO_REQUEST *req6=NULL;

 

        icmpv6 = (ICMPV6_HDR *)buf;

        req6   = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));

 

        req6->icmp6_echo_sequence = (USHORT)sequence;

    }

}
//  计算icmp校验和

void ComputeIcmpChecksum(SOCKET s, char *buf, int packetlen, struct addrinfo *dest)

{

    if (gAddressFamily == AF_INET)

    {

        ICMP_HDR    *icmpv4=NULL;

 

        icmpv4 = (ICMP_HDR *)buf;

        icmpv4->icmp_checksum = 0;

        icmpv4->icmp_checksum = checksum((USHORT *)buf, packetlen);

    }

    else if (gAddressFamily == AF_INET6)

    {

        ICMPV6_HDR  *icmpv6=NULL;

 

        icmpv6 = (ICMPV6_HDR *)buf;

        icmpv6->icmp6_checksum = 0;

        icmpv6->icmp6_checksum = ComputeIcmp6PseudoHeaderChecksum(

                s,

                buf,

                packetlen,

                dest

                );

    }

}

//  发布异步接收

int PostRecvfrom(SOCKET s, char *buf, int buflen, SOCKADDR *from, int *fromlen, WSAOVERLAPPED *ol)

{

    WSABUF  wbuf;

    DWORD   flags,

            bytes;

    int     rc;

 

    wbuf.buf = buf;

    wbuf.len = buflen;

 

    flags = 0;

 

    rc = WSARecvFrom(         //  通过重叠IO实现异步接收

            s,

           &wbuf,

            1,

           &bytes,

           &flags,

            from,

            fromlen,

            ol,

            NULL

            );

    if (rc == SOCKET_ERROR)

    {

        if (WSAGetLastError() != WSA_IO_PENDING)

        {

            fprintf(stderr, "WSARecvFrom failed: %d/n", WSAGetLastError());

            return SOCKET_ERROR;

        }

    }

    return NO_ERROR;

}

//  8

void PrintPayload(char *buf, int bytes)

{

    int     hdrlen=0,

            routes=0,

            i;

 

    UNREFERENCED_PARAMETER(bytes);

 

    if (gAddressFamily == AF_INET)

    {

        SOCKADDR_IN      hop;

        IPV4_OPTION_HDR *v4opt=NULL;

        IPV4_HDR        *v4hdr=NULL;

 

        hop.sin_family = (USHORT)gAddressFamily;

        hop.sin_port   = 0;

 

        v4hdr = (IPV4_HDR *)buf;

        hdrlen = (v4hdr->ip_verlen & 0x0F) * 4;  // 计算IP头部长度(8.1)

        if (hdrlen > sizeof(IPV4_HDR))   // 如果长度大于无选项IP头部长度

        {

                               // 选项头部指针

            v4opt = (IPV4_OPTION_HDR *)(buf + sizeof(IPV4_HDR));

                               //计算路由数(8.2)

            routes = (v4opt->opt_ptr / sizeof(ULONG)) - 1;

            for(i=0; i < routes ;i++)

            {

                hop.sin_addr.s_addr = v4opt->opt_addr[i];

                if (i == 0)

                    printf("    Route: ");

                else

                    printf("           ");

                PrintAddress((SOCKADDR *)&hop, sizeof(hop));

                                if (i < routes-1)

                    printf(" ->/n");

                else

                    printf("/n");

            }

        }

    }

    return;

}

(8.1) (v4hdr->ip_verlen & 0x0F) * 4  ip_verlen前4位为版本号,后四位为头部长度(4字节一记,因此要乘以4)。ip_verlen & 0x0F, 前四位屏蔽,后四位保留。
(8.2)  v4opt->opt_ptr 指向下一个可用地址,因此需要减一。

// 设置最大跳站数

int SetTtl(SOCKET s, int ttl)

{

    int     optlevel = 0,

            option = 0,

            rc;

 

    rc = NO_ERROR;

    if (gAddressFamily == AF_INET)

    {

        optlevel = IPPROTO_IP;

        option   = IP_TTL;

    }

    else if (gAddressFamily == AF_INET6)

    {

        optlevel = IPPROTO_IPV6;

        option   = IPV6_UNICAST_HOPS;

    }

    else

    {

        rc = SOCKET_ERROR;

    }

    if (rc == NO_ERROR)

    {

        rc = setsockopt(

                s,

                optlevel,

                option,

                (char *)&ttl,

                sizeof(ttl)

                );

    }

    if (rc == SOCKET_ERROR)

    {

        fprintf(stderr, "SetTtl: setsockopt failed: %d/n", WSAGetLastError());

    }

    return rc;

}

// 9

int __cdecl main(int argc, char **argv)

{

    WSADATA            wsd;

    WSAOVERLAPPED      recvol;        // 重叠 IO

    SOCKET             s=INVALID_SOCKET;

    char              *icmpbuf=NULL;

    struct addrinfo   *dest=NULL,

                      *local=NULL;

    IPV4_OPTION_HDR    ipopt;

    SOCKADDR_STORAGE   from;          // socket地址存储结构(9.1)

    DWORD              bytes,

                       flags;

    int                packetlen=0,

                       fromlen,

                       time=0,

                       rc,

                       i,

                       status = 0;

 

    recvol.hEvent = WSA_INVALID_EVENT;

 

    //  分析输入参数

    if (ValidateArgs(argc, argv) == FALSE)

    {

        status = -1;

        goto EXIT;

    }

 

    // socket模块启动初始化

    if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)

    {

        printf("WSAStartup() failed: %d/n", rc);

        status = -1;

        goto EXIT;

    }

 

    //  解析目的地址

    dest = ResolveAddress(

            gDestination,

            "0",

            gAddressFamily,

            0,

            0

            );

    if (dest == NULL)

    {

        printf("bad name %s/n", gDestination);

        status = -1;

        goto CLEANUP;

    }

    gAddressFamily = dest->ai_family;

 

    if (gAddressFamily == AF_INET)

        gProtocol = IPPROTO_ICMP;

    else if (gAddressFamily == AF_INET6)

        gProtocol = IPPROTO_ICMP6;

 

    // 获得本地地址,绑定使用

    local = ResolveAddress(

            NULL,

            "0",

            gAddressFamily,

            0,

            0

            );

    if (local == NULL)

    {

        printf("Unable to obtain the bind address!/n");

        status = -1;

        goto CLEANUP;

    }

 

    //创建Raw套接字,protocol = IPPROTO_ICMP

    s = socket(gAddressFamily, SOCK_RAW, gProtocol);           

    if (s == INVALID_SOCKET)

    {

        printf("socket failed: %d/n", WSAGetLastError());

        status = -1;

        goto CLEANUP;

    }

 

    SetTtl(s, gTtl); //设置最大跳站数为128

 

    if (gAddressFamily == AF_INET)

        packetlen += sizeof(ICMP_HDR);

    else if (gAddressFamily == AF_INET6)

        packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST);

 

    // packetlen 为  数据长度+ICMP头部长度

    packetlen += gDataSize;

 

    // 分配空间存储ICMP请求(9.2)

    icmpbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, packetlen);

    if (icmpbuf == NULL)

    {

        fprintf(stderr, "HeapAlloc failed: %d/n", GetLastError());

        status = -1;

        goto CLEANUP;

    }

 

    // 初始化 ICMP 头部

    if (gAddressFamily == AF_INET)

    {

        if (bRecordRoute) // 如有路由记录功能,初始化选项数据段

        {

            ZeroMemory(&ipopt, sizeof(ipopt));

            ipopt.opt_code = IP_RECORD_ROUTE; // 路由记录选项

            ipopt.opt_ptr  = 4;               // 指向可用的地址,每个地址占用4字节,此指针指向第一个可用的存储偏移地址

            ipopt.opt_len  = 39;              // 选项数据段长度

 

            rc = setsockopt(s, IPPROTO_IP, IP_OPTIONS,

                    (char *)&ipopt, sizeof(ipopt));

            if (rc == SOCKET_ERROR)

            {

                fprintf(stderr, "setsockopt(IP_OPTIONS) failed: %d/n", WSAGetLastError());

                status = -1;

                goto CLEANUP;

            }

        }

 

        InitIcmpHeader(icmpbuf, gDataSize);

    }

    else if (gAddressFamily == AF_INET6)

    {

        InitIcmp6Header(icmpbuf, gDataSize);

    }

 

    // 绑定地址,此套接字可获得外部单元向这个地址发送的数据

    rc = bind(s, local->ai_addr, (int)local->ai_addrlen);

    if (rc == SOCKET_ERROR)

    {

        fprintf(stderr, "bind failed: %d/n", WSAGetLastError());

        status = -1;

        goto CLEANUP;

    }

 

    // 建立接收操作

    memset(&recvol, 0, sizeof(recvol));

    recvol.hEvent = WSACreateEvent();    // 事件初始化

    if (recvol.hEvent == WSA_INVALID_EVENT)

    {

        fprintf(stderr, "WSACreateEvent failed: %d/n", WSAGetLastError());

        status = -1;

        goto CLEANUP;

    }

 

    //  异步接收

    fromlen = sizeof(from);

    PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);

 

    printf("/nPinging ");

    PrintAddress(dest->ai_addr, (int)dest->ai_addrlen);

    printf(" with %d bytes of data/n/n", gDataSize);

    // ping 4 次

    for(i=0; i < DEFAULT_SEND_COUNT ;i++)

    {

        // 设置序列号并计算校验和

        SetIcmpSequence(icmpbuf);

        ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);

 

        time = GetTickCount();

        rc = sendto(      // 发送icmp请求

                s,

                icmpbuf,

                packetlen,

                0,

                dest->ai_addr,

                (int)dest->ai_addrlen

                );

        if (rc == SOCKET_ERROR)

        {

            fprintf(stderr, "sendto failed: %d/n", WSAGetLastError());

            status = -1;

            goto CLEANUP;

        }

 

        // 等待ICMP回复(9.3)

        rc = WaitForSingleObject((HANDLE)recvol.hEvent, DEFAULT_RECV_TIMEOUT);

        if (rc == WAIT_FAILED)

        {

            fprintf(stderr, "WaitForSingleObject failed: %d/n", GetLastError());

            status = -1;

            goto CLEANUP;

        }

        else if (rc == WAIT_TIMEOUT)

        {

            printf("Request timed out./n");

        }

        else

        {

                   // 收到ICMP回复

            rc = WSAGetOverlappedResult(

                   s,

                   &recvol,

                   &bytes,

                    FALSE,

                   &flags

                    );

            if (rc == FALSE)

            {

                fprintf(stderr, "WSAGetOverlappedResult failed: %d/n", WSAGetLastError());

            }

            time = GetTickCount() - time;

 

            WSAResetEvent(recvol.hEvent);

 

            printf("Reply from ");

            PrintAddress((SOCKADDR *)&from, fromlen);

            if (time == 0)

                printf(": bytes=%d time<1ms TTL=%d/n", gDataSize, gTtl);

            else

                printf(": bytes=%d time=%dms TTL=%d/n", gDataSize, time, gTtl);

 

            PrintPayload(recvbuf, bytes);

 

            if (i < DEFAULT_SEND_COUNT - 1)

            {

                fromlen = sizeof(from);

                PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);

            }

        }

        Sleep(1000);

    }

 

CLEANUP:

   

    if (dest)

         freeaddrinfo(dest);

    if (local)

         freeaddrinfo(local);

    if (s != INVALID_SOCKET)

        closesocket(s);

    if (recvol.hEvent != WSA_INVALID_EVENT)

        WSACloseEvent(recvol.hEvent);

    if (icmpbuf)

        HeapFree(GetProcessHeap(), 0, icmpbuf);

 

    WSACleanup();

 

EXIT:

 

    system("pause");

 

    return status;

}

(9.1) sockaddr 以及 sockaddr_in比较:这两个结构大小相同全为16字节,可相互转化。唯一区别是sockaddr_in将内容分得更细,sockaddr结构一般作为参数带入,sockaddr_in结构在填值时使用。

struct sockaddr {

        u_short sa_family;             // 2字节

        char    sa_data[14];           // 14字节

};

 

typedef struct addrinfo

{

int                 ai_flags;      

int                 ai_family;      

    int                 ai_socktype;   

    int                 ai_protocol;   

    size_t              ai_addrlen;    

    char *              ai_canonname;  

    struct sockaddr *   ai_addr;      

    struct addrinfo *   ai_next;      

};

 

struct sockaddr_in {

        short   sin_family;         // 2字节

        u_short sin_port;           // 2字节

        struct  in_addr sin_addr;   // 4字节

        char    sin_zero[8];        // 8字节填充

};

struct in_addr {      // 4字节

        union {

                struct { u_char s_b1,s_b2,s_b3,s_b4; }                     S_un_b;

                struct { u_short s_w1,s_w2; } S_un_w;

                u_long S_addr;

        } S_un;

};

(9.2)  HeapAlloc 和 malloc的功能一样,都是在堆上面分配空间。HeapAlloc为Windows SDK中提供的函数,malloc是anci C中的函数。后者更有可移植性。

(9.3) 此程序首先发起异步接收后再发送ICMP请求包可以保证发送端不会漏掉任何ICMP回应。UDP协议下先发送后接收的方式会因为接收端运行速度慢的问题导致丢包。

时间: 2024-12-27 11:43:30

Windows平台Ping示例源码分析(C/C++)的相关文章

java io学习(三) 管道的简介,源码分析和示例

管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例 本章,我们对java 管道进行学习. java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流. 它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用. 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStr

java io学习(二)ByteArrayOutputStream的简介,源码分析和示例

ByteArrayOutputStream的简介,源码分析和示例(包括OutputStream) 前面学习ByteArrayInputStream,了解了"输入流".接下来,我们学习与ByteArrayInputStream相对应的输出流,即ByteArrayOutputStream. 本章,我们会先对ByteArrayOutputStream进行介绍,在了解了它的源码之后,再通过示例来掌握如何使用它. ByteArrayOutputStream 介绍 ByteArrayOutputS

java io学习(一)ByteArrayInputStream的简介,源码分析和示例

ByteArrayInputStream的简介,源码分析和示例(包括InputStream) 我们以ByteArrayInputStream,拉开对字节类型的"输入流"的学习序幕. 本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的用法. ByteArrayInputStream 介绍 ByteArrayInputStream 是字节数组输入流.它继承于InputStream. 它包含一个内部缓冲区,该缓冲区包含从流中读取

Hadoop2源码分析-YARN RPC 示例介绍

1.概述 之前在<Hadoop2源码分析-RPC探索实战>一文当中介绍了Hadoop的RPC机制,今天给大家分享关于YARN的RPC的机制.下面是今天的分享目录: YARN的RPC介绍 YARN的RPC示例 截图预览 下面开始今天的内容分享. 2.YARN的RPC介绍 我们知道在Hadoop的RPC当中,其主要由RPC,Client及Server这三个大类组成,分别实现对外提供编程接口.客户端实现及服务端实现.如下图所示:     图中是Hadoop的RPC的一个类的关系图,大家可以到<

java io系列03之 ByteArrayOutputStream的简介,源码分析和示例(包括OutputStream)

前面学习ByteArrayInputStream,了解了"输入流".接下来,我们学习与ByteArrayInputStream相对应的输出流,即ByteArrayOutputStream.本章,我们会先对ByteArrayOutputStream进行介绍,在了解了它的源码之后,再通过示例来掌握如何使用它. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_03.html ByteArrayOutputStream 介绍 ByteArray

java io系列04之 管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例

本章,我们对java 管道进行学习. 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_04.html java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流.它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用.使 用管道通信时,大致的流程是:我们在线程A中向PipedOut

java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)

  我们以ByteArrayInputStream,拉开对字节类型的"输入流"的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的用法.   转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_02.html   ByteArrayInputStream 介绍   ByteArrayInputStream 是字节数组输入流.它继承于InputStream.它包含一个内

Android 源码分析,FreeMind 是一件超级利器

Android 源码分析,FreeMind 是一件超级利器 思维导图软件 XMind 与 FreeMind 的对比 作者: 善用佳软 日期: 2012-04-17 分类: 1 文本办公, 1.5 思维导图 标签: mindmap 思维导图类软件中,最有影响力的开源免费软件是 FreeMind 和 XMind.FreeMind历史悠久,当属经典:XMind作为后起之秀,大有赶超之势.同作为免费.开源的思维导图解决方案,应如何选择/结合两款软件?本文试做分析,以供用户/开发者参考. 本文的分析基于W

深入理解Spark:核心思想与源码分析

大数据技术丛书 深入理解Spark:核心思想与源码分析 耿嘉安 著 图书在版编目(CIP)数据 深入理解Spark:核心思想与源码分析/耿嘉安著. -北京:机械工业出版社,2015.12 (大数据技术丛书) ISBN 978-7-111-52234-8 I. 深- II.耿- III.数据处理软件 IV. TP274 中国版本图书馆CIP数据核字(2015)第280808号 深入理解Spark:核心思想与源码分析 出版发行:机械工业出版社(北京市西城区百万庄大街22号 邮政编码:100037)