「Ping」WinSock实作Ping

// IP 结构

typedef struct _iphdr

{

    unsigned int   h_len:4;        // IP首部长度

    unsigned int   version:4;      // IP协议版本

    unsigned char  tos;            // 服务类型

    unsigned short total_len;      // 总数据包大小

    unsigned short ident;          // 唯一识别符

    unsigned short frag_and_flags; // 旗标

    unsigned char  ttl;            // TTL生存时间

    unsigned char  proto;          // 协议 (TCP, UDP )

    unsigned short checksum;       // IP 检验和

 

    unsigned int   sourceIP;       // IP地址

    unsigned int   destIP;         // 目的IP地址

} IpHeader;

 

// ICMP 结构

typedef struct _icmphdr

{

    BYTE   i_type;                 // ICMP 报文类型

    BYTE   i_code;                 // ICMP 代码

    USHORT i_cksum;                // 检验和

    USHORT i_id;                   // 标识符

    USHORT i_seq;                  // 序列号

    // 预留空间

    ULONG  timestamp;              // 时间戳

} IcmpHeader;

 

//    构造ICMP回应请求报头

void  FillICMPData(char *icmp_data, int datasize)

{

    IcmpHeader *icmp_hdr = NULL;

    char       *datapart = NULL;

 

    icmp_hdr = (IcmpHeader*)icmp_data;

    icmp_hdr->i_type = 8;        // 请求一个ICMP回应

    icmp_hdr->i_code = 0;        // 代码字段 0

    icmp_hdr->i_id = (USHORT)GetCurrentProcessId();  // 获取当前进程一个唯一的标识符,填充为ICMP标识符

    icmp_hdr->i_cksum = 0;       // 检验和置零

    icmp_hdr->i_seq = 0;         // 序列号

    // icmp_data 加上头部大小,datapart就指向ICMP数据包中的具体内容

    datapart = icmp_data + sizeof(IcmpHeader); 

    // 在数据包正文中填充无用字符 E

    memset(datapart,'E', datasize - sizeof(IcmpHeader));

}

 

//  计算检验和(各字节相加,再取反)

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

}

 

// 解析ICMP回应报头

INT DecodeICMPHeader(char *buf, int bytes)

{

    IpHeader       *iphdr = NULL;

    IcmpHeader     *icmphdr = NULL;

    unsigned short  iphdrlen;

    DWORD           tick;

 

    tick = GetTickCount();

    iphdr = (IpHeader *)buf;

    // Number of 32-bit words * 4 = bytes

    iphdrlen = iphdr->h_len * 4;

    // 回应的包字节不够

    if (bytes  < (iphdrlen + 8)) return -1;

    icmphdr = (IcmpHeader*)(buf + iphdrlen);

    // 回应类型表示未成功

    if (icmphdr->i_type != 0) return -1;

    // 唯一标识号不匹配,别人的包

    if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) return -1;

   

    // 返回耗费时间,毫秒

    return tick - icmphdr->timestamp;

}

 

INT  Ping(LPSTR IP)

{

    WSADATA            wsaData;

    SOCKET             sockRaw;

    sockaddr_in        dest;

    INT                bread,

                       datasize = 32,   // 默认包大小

                       timeout  = 1000; // 超时时间

    CHAR              *icmp_data = NULL,

                      *recvbuf = NULL;

    USHORT             seq_no = 0;

 

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return -1;

    sockRaw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);

    if (sockRaw == INVALID_SOCKET)

    {

        if (WSAGetLastError() == 10013) 

        { 

            WSACleanup();

            return -2;  // 创建原始套接字权限不够

        }

        else{ 

            WSACleanup();

            return -3;  // 未知错误

        }

       

    }

 

    bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));

    if(bread == SOCKET_ERROR)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -4;   // 设置套接字选项失败

    }

    bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));

    if(bread == SOCKET_ERROR)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -4;   // 设置套接字选项失败

    }

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

    dest.sin_family = AF_INET;

    dest.sin_addr.S_un.S_addr = inet_addr(IP);

 

    datasize += sizeof(IcmpHeader);

 

    icmp_data = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,1024);

    recvbuf = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,1024);

    if (!icmp_data)

    {

        closesocket(sockRaw);

        WSACleanup();

        return -5;  // 堆内存分配失败

    }

    if (!recvbuf)  

    {

        HeapFree(GetProcessHeap(), 0, recvbuf);

        closesocket(sockRaw);

        WSACleanup();

        return -5;  // 堆内存分配失败

    }

   

    memset(icmp_data,0,1024);

    FillICMPData(icmp_data,datasize);

 

    INT nCount,utime;  // 尝试 ping 4

    for (nCount = 0;nCount != 4; nCount++)

    {

        INT bwrote;

        ((IcmpHeader*)icmp_data)->i_cksum = 0;  // 检验和置零

        ((IcmpHeader*)icmp_data)->i_seq = seq_no++;  // ICMP包序列号

        // 返回从操作系统启动到现在所经过的毫秒数

        ((IcmpHeader*)icmp_data)->timestamp = GetTickCount(); // 本地发送时间戳

        // 计算检验和(注意这里,一定要最后计算,哈哈~我为了让时间更准确,就犯了这个错误)

        ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);

 

        bwrote = sendto(sockRaw, icmp_data, datasize, 0, (sockaddr*)&dest, sizeof(dest));

        if (bwrote == SOCKET_ERROR)

        {

            if (WSAGetLastError() == WSAETIMEDOUT)  // 超时

                continue;

            HeapFree(GetProcessHeap(), 0, recvbuf);

            HeapFree(GetProcessHeap(), 0, icmp_data);

            closesocket(sockRaw);

            WSACleanup();

            return -6;   // 发送ICMP报头失败

        }

        bread = recv(sockRaw, recvbuf, 1024, 0);

        if (bread == SOCKET_ERROR)

        {

            if (WSAGetLastError() == WSAETIMEDOUT)  // 超时

                continue;

            HeapFree(GetProcessHeap(), 0, recvbuf);

            HeapFree(GetProcessHeap(), 0, icmp_data);

            closesocket(sockRaw);

            WSACleanup();

            return -7;   // 接收ICMP回应失败

        }

        utime = DecodeICMPHeader(recvbuf,bread);

        if (utime != -1)

            break;

        Sleep(300);

    }

 

    closesocket(sockRaw);

    HeapFree(GetProcessHeap(), 0, recvbuf);

    HeapFree(GetProcessHeap(), 0, icmp_data);

    WSACleanup();

    if(nCount == 4)

        return -8;  // 超时

    else

        return utime;  // 成功,返回耗时

}

 

 

   使用原始套接字需要管理员权限.不然就会返回10013错误代码.

你可能感兴趣的:(socket,struct,tcp,null,buffer,byte)