前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ping 实现设计---ICMP

ping 实现设计---ICMP

作者头像
用户1154259
发布2018-01-17 15:49:23
1.4K0
发布2018-01-17 15:49:23
举报

发送ICMP报文时,必须程序自己计算校验和,将它填入ICMP头部对应的域中。

校验和的计算方法:

  将数据以字为单位累加到一个双字中,如果数据长度为奇数,最后一个字节将被扩展到字,累加的结果是一个双字,最后将这个双字的高16位,低16位相加后取反,便得到了校验和。

下面是checksum的计算校验和的代码:

代码语言:javascript
复制
USHORT checksum(USHORT* buff, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += *buff++;
        size -= sizeof(USHORT);
    }
    // 是奇数
    if(size)
    {
        cksum += *(UCHAR*)buff;
    }
    // 将32位的chsum高16位和低16位相加,然后取反
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);            
    return (USHORT)(~cksum);
}

Ping程序实例:

Ping用来检查主机是否存在,是否可达。

下面是Ping的执行步骤:

1 创建协议类型为IPPROTO_ICMP的原始套接字

2 创建并初始化ICMP封包

3 调用sendto函数向远程主机发送ICMP请求

4 调用recvfrom函数接收ICMP响应

完整代码如下:

代码语言:javascript
复制
///////////////////////////////////////////
// ping.cpp文件

#include "../common/initsock.h"
#include "../common/protoinfo.h"
#include <stdio.h>
#include <winsock2.h>
#include <windows.h>
#include "Ws2tcpip.h"
CInitSock theSock;

typedef struct icmp_hdr
{
    unsigned char   icmp_type;        // 消息类型
    unsigned char   icmp_code;        // 代码
    unsigned short  icmp_checksum;    // 校验和
    // 下面是回显头
    unsigned short  icmp_id;        // 用来惟一标识此请求的ID号,通常设置为进程ID
    unsigned short  icmp_sequence;    // 序列号
    unsigned long   icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR;

USHORT checksum(USHORT* buff, int size);
BOOL SetTTL(SOCKET s, int nValue);
BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv = TRUE);

USHORT checksum(USHORT* buff, int size)
{
    unsigned long cksum = 0;
    while(size>1)
    {
        cksum += *buff++;
        size -= sizeof(USHORT);
    }
    // 是奇数
    if(size)
    {
        cksum += *(UCHAR*)buff;
    }
    // 将32位的chsum高16位和低16位相加,然后取反
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);            
    return (USHORT)(~cksum);
}

BOOL SetTTL(SOCKET s, int nValue)
{
    int ret = ::setsockopt(s, IPPROTO_IP, IP_TTL, (char*)&nValue, sizeof(nValue));
    return ret != SOCKET_ERROR;
}

BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv)
{
    int ret = ::setsockopt(s, SOL_SOCKET, 
        bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char*)&nTime, sizeof(nTime));
    return ret != SOCKET_ERROR;
}




int main()
{
    // 目的IP地址,即要Ping的IP地址
    char szDestIp[] = "127.0.0.1";    // 127.0.0.1

    // 创建原始套节字
    SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

    // 设置接收超时
    SetTimeout(sRaw, 1000, TRUE);

    // 设置目的地址
    SOCKADDR_IN dest;
    dest.sin_family = AF_INET;
    dest.sin_port = htons(0);
    dest.sin_addr.S_un.S_addr = inet_addr(szDestIp);

    // 创建ICMP封包
    char buff[sizeof(ICMP_HDR) + 32];
    ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
    // 填写ICMP封包数据
    pIcmp->icmp_type = 8;    // 请求一个ICMP回显
    pIcmp->icmp_code = 0;
    pIcmp->icmp_id = (USHORT)::GetCurrentProcessId();
    pIcmp->icmp_checksum = 0;
    pIcmp->icmp_sequence = 0;
    // 填充数据部分,可以为任意
    memset(&buff[sizeof(ICMP_HDR)], 'E', 32);
    
    // 开始发送和接收ICMP封包
    USHORT    nSeq = 0;
    char recvBuf[1024];
    SOCKADDR_IN from;
    int nLen = sizeof(from);
    while(TRUE)
    {
        static int nCount = 0;
        int nRet;
        if(nCount++ == 4)
            break;
        pIcmp->icmp_checksum = 0;
        pIcmp->icmp_timestamp = ::GetTickCount();
        pIcmp->icmp_sequence = nSeq++;
        pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32);
        nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));
        if(nRet == SOCKET_ERROR)
        {
            printf(" sendto() failed: %d \n", ::WSAGetLastError());
            return -1;
        }
        nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
        if(nRet == SOCKET_ERROR)
        {
            if(::WSAGetLastError() == WSAETIMEDOUT)
            {
                printf(" timed out\n");
                continue;
            }
            printf(" recvfrom() failed: %d\n", ::WSAGetLastError());
            return -1;
        }

        // 下面开始解析接收到的ICMP封包
        int nTick = ::GetTickCount();
        if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))
        {
            printf(" Too few bytes from %s \n", ::inet_ntoa(from.sin_addr));
        }
        // 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头
        ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
        if(pRecvIcmp->icmp_type != 0)    // 回显
        {
            printf(" nonecho type %d recvd \n", pRecvIcmp->icmp_type);
            return -1;
        }

        if(pRecvIcmp->icmp_id != ::GetCurrentProcessId())
        {
            printf(" someone else's packet! \n");
            return -1;
        }
        
        printf(" %d bytes from %s:", nRet, inet_ntoa(from.sin_addr));
        printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);
        printf(" time: %d ms", nTick - pRecvIcmp->icmp_timestamp);
        printf(" \n");

        ::Sleep(1000);
    }

    return 0;
}

执行结果:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2012-10-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档