现在的位置: 首页 > 综合 > 正文

如何编码实现Windows下的ping功能

2013年08月15日 ⁄ 综合 ⁄ 共 8616字 ⁄ 字号 评论关闭

一、数据结构

首先根据IP数据包格式(图下图)定义IP数据包头的数据结构

IP数据包

然后根据ICMP回送请求与应答报文格式定义ICMP的数据结构

ICMP回送请求与应答

 

然后分别定义请求回送的数据长度

请求回送的数据结构

ICMP回送应答的数据结构

 

二、函数实现

(1)SendEchoRequest

函数功能是发送回送请求数据包,首先定义三个静态变量

    static ECHOREQUEST echoReq;    // 回送请求数据结构
    static nId = 1;        // 标识符
    static nSeq = 1;    // 序号

然后填写回送请求信息

    echoReq.icmpHdr.Type = ICMP_ECHOREQ; // 类型
    echoReq.icmpHdr.Code = 0;             // 代码
    echoReq.icmpHdr.Checksum = 0;         // 校验和
    echoReq.icmpHdr.ID = nId++;             // 标识符

    echoReq.icmpHdr.Seq = nSeq++;         // 序号

填写要发送的数据
    for (i = 0; i < REQ_DATASIZE; i++)
    {
        echoReq.cData[i] = ' ' + i;
    }

保存发送时间
    echoReq.dwTime = GetTickCount();

 数据存入包中并计算校验和
    echoReq.icmpHdr.Checksum = in_chsum((u_short*)&echoReq, sizeof(ECHOREQUEST));

发送回送请求
    nRet = sendto(s,
        (LPSTR)&echoReq,
        sizeof(ECHOREQUEST),
        0,
        (LPSOCKADDR)lpstToAddr,
        sizeof(SOCKADDR_IN));

(2)RecvEchoReply

函数功能为接收回送应答数据

 

 
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
    ECHOREPLY echoReply;    // 回送应答数据结构
    int nRet;
    int nAddrLen = sizeof(sockaddr_in);
    // 接受回送应答
    nRet = recvfrom(s,
                    (LPSTR)&echoReply,
                    sizeof(ECHOREPLY),
                    0,
                    (LPSOCKADDR)lpsaFrom,
                    &nAddrLen);
    // 检查返回的值
    if (nRet == SOCKET_ERROR)
    {
        ReportError("recvfrom()");
    }
    *pTTL = echoReply.ipHdr.TTL;    // 取得TTL值
    return (echoReply.echoRequest.dwTime);    // 返回所用时间
}
(3)WaitForEchoReply

函数功能:等待套接子s是否有数据可读
int WaitForEchoReply(SOCKET s)
{
    timeval Timeout;
    fd_set readfds;
    readfds.fd_count = 1;
    readfds.fd_array[0] = s;
    Timeout.tv_sec = 5;
    Timeout.tv_usec = 0;
    return (select(1, &readfds, NULL, NULL, &Timeout));
}

 

(3)in_chsum

函数功能计算校验和

u_short in_chsum(u_short *addr, int len)
{
    register int nLeft = len;
    register u_short *w = addr;
    register u_short answer;
    register int sum = 0;
    while (nLeft > 1)
    {
        sum += *w++;
        nLeft -= 2;
    }

    if (nLeft == 1)
    {
        u_short u = 0;
        *(u_char*)(&u) = *(u_char*)w;
        sum += u;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return (answer);
}

(4)main函数的实现

第一步:定义Winsock数据结构wsaData并新建版本号1.1

第二步:调用WSAStartup初始化wsaData

第三步:调用Ping函数

第四步:调用WSACleanup释放Winsock

void main(int argc, char **argv)
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(1, 1); // Winsock1.1
    int nRet;
   
    // 命令行参数检查
    if (argc != 2)
    {
        fprintf(stderr, "/nUsage: ping hostname/n");
        return;
    }

    // 初始化Winsock
    nRet = WSAStartup(wVersionRequested, &wsaData);
    if (nRet)
    {
        fprintf(stderr, "/nError initializing Winsock/n");
        return;
    }

    if (wsaData.wVersion != wVersionRequested)
    {
        fprintf(stderr, "/nWinsock version not supported/n");
        return;
    }
    // 调用ping函数
    Ping(argv[1]);
    //Ping("www.sina.com");
    // 释放Winsock
    WSACleanup();
}

(5)Ping

函数功能:实现ping功能

定义函数用到的数据

    SOCKET rawSocket;    // 原始套接字
    LPHOSTENT lpHost;    // 主机信息
    sockaddr_in saDest;    // 目的地址
    sockaddr_in saSrc;    // 源地址
    DWORD dwTimeSent;    // 发送时间
    DWORD dwElapsed;    // 延迟时间

然后创建一个原始套接字

创建一个原始套接口,协议为ICMP协议
    rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

根据用户输入的目的地址获取

lpHost = gethostbyname(pstrHost);

设置目标套接口地址
    saDest.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr));
    saDest.sin_family = AF_INET;
    saDest.sin_port = 0;

 输出ping程序的提示信息
    printf("/nPinging %s [%s] with %d bytes of data:/n",
                pstrHost,
                inet_ntoa(saDest.sin_addr),
                REQ_DATASIZE);

发送ICMP回送请求
        SendEchoRequest(rawSocket, &saDest);
使用select()等待接收回送的数据

        WaitForEchoReply(rawSocket);
接收应答
        dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
计算传输时间,并输出提示信息
        dwElapsed = GetTickCount() - dwTimeSent;

答应应答信息
       printf("/nReply from: %s: bytes=%d time=%ldms TTL=%d",
                inet_ntoa(saSrc.sin_addr),
                REQ_DATASIZE,
                dwElapsed,
                cTTL);
    }
    // 关闭套接字
    nRet = closesocket(rawSocket);

附:程序源代码

 

【上篇】
【下篇】

抱歉!评论已关闭.