如何編碼實現Windows下的ping功能

來源:互聯網
上載者:User

一、資料結構

首先根據IP資料包格式(圖)定義IP資料包頭的資料結構

typedef struct tagIPHDR// IP資料包頭部<br />{<br />u_char VIHL;// 版本號碼(4)+頭長度(4)<br />u_char TOS;// 服務類型(8)<br />short TotLen;// 總長度(16)<br />short ID;// 標識(16)<br />short FlagOff;// 標誌(3)+片位移(13)<br />u_char TTL;// 存留時間TTL(8)<br />u_short CheckSum;// 頭部校正和(16)<br />in_addr iaSrc;// 源IP地址(32)<br />in_addr iaDst;// 目標IP地址(32)<br />} IPHDR, *PIPHDR;

然後根據ICMP回送請求與應答報文格式定義ICMP的資料結構

typedef struct tagICMPHDR// ICMP回送請求與應帶ICMP報文<br />{<br />u_char Type;// 類型(8)<br />u_char Code;// 代碼(8)<br />u_short Checksum;// 校正和(16)<br />u_short ID;// 標識符(16)<br />u_short Seq;// 序號(16)<br />char Data;// 任選資料<br />} ICMPHDR, *PICMPHDR;

 

然後分別定義請求回送的資料長度

#define REQ_DATASIZE 32

請求回送的資料結構

typedef struct tagECHOREQUEST<br />{<br />ICMPHDR icmpHdr;<br />DWORD dwTime;<br />char cData[REQ_DATASIZE];<br />} ECHOREQUEST, *PECHOREQUEST;

ICMP回送應答的資料結構

typedef struct tagECHOREPLY<br />{<br />IPHDR ipHdr;<br />ECHOREQUEST echoRequest;<br />char cFiller[256];<br />} ECHOREPLY, *PECHOREPLY;

 

二、函數實現

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

附:程式原始碼

// ping.h<br />// 在該標頭檔中定義了IP和ICMP協議頭的結構<br />#pragma pack(1)<br />#define ICMP_ECHOREPLY 0<br />#define ICMP_ECHOREQ 8<br />typedef struct tagIPHDR// IP資料包頭部<br />{<br />u_char VIHL;// 版本號碼(4)+頭長度(4)<br />u_char TOS;// 服務類型(8)<br />short TotLen;// 總長度(16)<br />short ID;// 標識(16)<br />short FlagOff;// 標誌(3)+片位移(13)<br />u_char TTL;// 存留時間TTL(8)<br />u_short CheckSum;// 頭部校正和(16)<br />in_addr iaSrc;// 源IP地址(32)<br />in_addr iaDst;// 目標IP地址(32)<br />} IPHDR, *PIPHDR;<br />typedef struct tagICMPHDR// ICMP回送請求與應帶ICMP報文<br />{<br />u_char Type;// 類型(8)<br />u_char Code;// 代碼(8)<br />u_short Checksum;// 校正和(16)<br />u_short ID;// 標識符(16)<br />u_short Seq;// 序號(16)<br />char Data;// 任選資料<br />} ICMPHDR, *PICMPHDR;<br />// 請求回送的資料長度<br />#define REQ_DATASIZE 32<br />// ICMP回送請求的資料結構<br />typedef struct tagECHOREQUEST<br />{<br />ICMPHDR icmpHdr;<br />DWORD dwTime;<br />char cData[REQ_DATASIZE];<br />} ECHOREQUEST, *PECHOREQUEST;<br />// ICMP回送應答<br />typedef struct tagECHOREPLY<br />{<br />IPHDR ipHdr;<br />ECHOREQUEST echoRequest;<br />char cFiller[256];<br />} ECHOREPLY, *PECHOREPLY;<br />#pragma pack()

 

// ping.cpp<br />// 實現簡易ping功能<br />#include <stdio.h><br />#include <stdlib.h><br />#include <winsock.h><br />#include "ping.h"<br />void Ping(LPCSTR pstrHost);<br />void ReportError(LPCSTR pstrFrom);<br />int WaitForEchoReply(SOCKET s);<br />u_short in_chsum(u_short *addr, int len);<br />// ICMP 回送請求和應答函式宣告<br />int SendEchoRequest(SOCKET, LPSOCKADDR_IN);<br />DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);<br />// 主程式<br />void main(int argc, char **argv)<br />{<br />WSADATA wsaData;<br />WORD wVersionRequested = MAKEWORD(1, 1); // Winsock1.1<br />int nRet;</p><p>// 命令列參數檢查<br />if (argc != 2)<br />{<br />fprintf(stderr, "/nUsage: ping hostname/n");<br />return;<br />}<br />// 初始化Winsock<br />nRet = WSAStartup(wVersionRequested, &wsaData);<br />if (nRet)<br />{<br />fprintf(stderr, "/nError initializing Winsock/n");<br />return;<br />}<br />if (wsaData.wVersion != wVersionRequested)<br />{<br />fprintf(stderr, "/nWinsock version not supported/n");<br />return;<br />}<br />// 調用ping函數<br />Ping(argv[1]);<br />//Ping("www.sina.com");<br />// 釋放Winsock<br />WSACleanup();<br />}<br />void Ping(LPCSTR pstrHost)<br />{<br />SOCKET rawSocket;// 原始通訊端<br />LPHOSTENT lpHost;// 主機資訊<br />sockaddr_in saDest;// 目的地址<br />sockaddr_in saSrc;// 源地址<br />DWORD dwTimeSent;// 發送時間<br />DWORD dwElapsed;// 延遲時間<br />u_char cTTL;<br />int nLoop;<br />int nRet;<br />// 建立一個原始套介面<br />rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);<br />if (rawSocket == SOCKET_ERROR)<br />{<br />ReportError("socket()");<br />return;<br />}<br />lpHost = gethostbyname(pstrHost);<br />if (lpHost == NULL)<br />{<br />fprintf(stderr, "/nHost not found: %s/n", pstrHost);<br />return;<br />}<br />// 設定目標套介面地址<br />saDest.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr));<br />saDest.sin_family = AF_INET;<br />saDest.sin_port = 0;<br />// 輸出ping程式的提示資訊<br />printf("/nPinging %s [%s] with %d bytes of data:/n",<br />pstrHost,<br />inet_ntoa(saDest.sin_addr),<br />REQ_DATASIZE);<br />// 控制ping執行的次數<br />for (nLoop = 0; nLoop < 4; nLoop++)<br />{<br />// 發送ICMP回送請求<br />SendEchoRequest(rawSocket, &saDest);<br />// 使用select()等待接收回送的資料<br />nRet = WaitForEchoReply(rawSocket);<br />if (nRet == SOCKET_ERROR)<br />{<br />ReportError("select()");<br />break;<br />}<br />if (!nRet)<br />{<br />printf("/nTimeOut/n");<br />break;<br />}<br />// 接收應答<br />dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);<br />// 計算傳輸時間,並輸出提示資訊<br />dwElapsed = GetTickCount() - dwTimeSent;<br />printf("/nReply from: %s: bytes=%d time=%ldms TTL=%d",<br />inet_ntoa(saSrc.sin_addr),<br />REQ_DATASIZE,<br />dwElapsed,<br />cTTL);<br />}<br />printf("/n");<br />// 關閉通訊端<br />nRet = closesocket(rawSocket);<br />if (nRet == SOCKET_ERROR)<br />{<br />ReportError("closesocket()");<br />}<br />}<br />int SendEchoRequest(SOCKET s, LPSOCKADDR_IN lpstToAddr)<br />{<br />static ECHOREQUEST echoReq;// 回送請求資料結構<br />static nId = 1;// 標識符<br />static nSeq = 1;// 序號<br />int nRet;<br />int i;<br />// 填寫回送請求資訊<br />echoReq.icmpHdr.Type = ICMP_ECHOREQ; // 類型<br />echoReq.icmpHdr.Code = 0; // 代碼<br />echoReq.icmpHdr.Checksum = 0; // 校正和<br />echoReq.icmpHdr.ID = nId++; // 標識符<br />echoReq.icmpHdr.Seq = nSeq++; // 序號<br />// 填寫要發送的資料<br />for (i = 0; i < REQ_DATASIZE; i++)<br />{<br />echoReq.cData[i] = ' ' + i;<br />}<br />// 儲存發送時間<br />echoReq.dwTime = GetTickCount();<br />// 資料存入包中並計算校正和<br />echoReq.icmpHdr.Checksum = in_chsum((u_short*)&echoReq, sizeof(ECHOREQUEST));<br />// 發送回送請求<br />nRet = sendto(s,<br />(LPSTR)&echoReq,<br />sizeof(ECHOREQUEST),<br />0,<br />(LPSOCKADDR)lpstToAddr,<br />sizeof(SOCKADDR_IN));<br />if (nRet == SOCKET_ERROR)<br />{<br />ReportError("sendto()");<br />}<br />return nRet;<br />}<br />DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)<br />{<br />ECHOREPLY echoReply;// 回送應答資料結構<br />int nRet;<br />int nAddrLen = sizeof(sockaddr_in);<br />// 接受回送應答<br />nRet = recvfrom(s,<br />(LPSTR)&echoReply,<br />sizeof(ECHOREPLY),<br />0,<br />(LPSOCKADDR)lpsaFrom,<br />&nAddrLen);<br />// 檢查返回的值<br />if (nRet == SOCKET_ERROR)<br />{<br />ReportError("recvfrom()");<br />}<br />*pTTL = echoReply.ipHdr.TTL;// 取得TTL值<br />return (echoReply.echoRequest.dwTime);// 返回所用時間<br />}<br />void ReportError(LPCSTR pstrFrom)<br />{<br />fprintf(stderr, "/n %d error: /n", WSAGetLastError());<br />}<br />// 等待套接子s是否有資料可讀<br />int WaitForEchoReply(SOCKET s)<br />{<br />timeval Timeout;<br />fd_set readfds;<br />readfds.fd_count = 1;<br />readfds.fd_array[0] = s;<br />Timeout.tv_sec = 5;<br />Timeout.tv_usec = 0;<br />return (select(1, &readfds, NULL, NULL, &Timeout));<br />}<br />u_short in_chsum(u_short *addr, int len)<br />{<br />register int nLeft = len;<br />register u_short *w = addr;<br />register u_short answer;<br />register int sum = 0;<br />while (nLeft > 1)<br />{<br />sum += *w++;<br />nLeft -= 2;<br />}<br />if (nLeft == 1)<br />{<br />u_short u = 0;<br />*(u_char*)(&u) = *(u_char*)w;<br />sum += u;<br />}<br />sum = (sum >> 16) + (sum & 0xffff);<br />sum += (sum >> 16);<br />answer = ~sum;<br />return (answer);<br />}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.