C++實現PING命令

#include
#include <stdlib.h>
#include <winsock.h>
#pragma  comment (lib, "ws2_32.lib")

#define ICMP_ECHOREPLY 0  // ICMP回覆應答

#define ICMP_ECHOREQ 8  // ICMP迴應請求

#define REQ_DATASIZE 32 // 請求數據報大小

// 定義IP首部格式
typedef struct _IPHeader
{
u_char  VIHL; // 版本和首部長度
u_char ToS; // 服務類型
u_short TotalLen; // 總長度
u_short ID; // 標識號
u_short Frag_Flags; // 段偏移量
u_char TTL; // 生存時間
u_char Protocol; // 協議
u_short Checksum; // 首部校驗和
struct in_addr SrcIP; // 源IP地址
struct in_addr DestIP; // 目的地址
}IPHDR, *PIPHDR;
// 定義ICMP首部格式
typedef struct _ICMPHeader
{
u_char Type; // 類型
u_char Code; // 代碼
u_short Checksum; // 首部校驗和
u_short ID; // 標識
u_short Seq; // 序列號
char Data; // 數據
}ICMPHDR, *PICMPHDR;
// 定義ICMP迴應請求
typedef struct _ECHOREQUEST
{
ICMPHDR icmpHdr;
DWORD dwTime;
char cData[REQ_DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;


// 定義ICMP迴應答覆 
typedef struct _ECHOREPLY
{
IPHDR ipHdr;
ECHOREQUEST echoRequest;
char    cFiller[256];
}ECHOREPLY, *PECHOREPLY;


// 計算校驗和
u_short checksum(u_short *buffer, int len)
{
register int nleft = len;
register u_short *w = buffer;
register u_short answer;
register int sum = 0;
    // 使用32bit的累加器,進行16bit的反饋計算
while( nleft > 1 )  {
sum += *w++;
nleft -= 2;
}
// 補全奇數位
if( nleft == 1 ) {
u_short u = 0;


*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
   // 將反饋的16bit從高位移至地位
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
// 發送迴應請求函數
int SendEchoRequest(SOCKET s,struct sockaddr_in *lpstToAddr) 
{
static ECHOREQUEST echoReq;
static nId = 1;
static nSeq = 1;
int nRet;
// 填充迴應請求消息
echoReq.icmpHdr.Type = ICMP_ECHOREQ;
echoReq.icmpHdr.Code = 0;
echoReq.icmpHdr.Checksum = 0;
echoReq.icmpHdr.ID = nId++;
echoReq.icmpHdr.Seq = nSeq++;
// 填充要發送的數據(隨便填寫)
for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
echoReq.cData[nRet] = ' '+nRet;
// 儲存發送的時間
echoReq.dwTime = GetTickCount();
// 計算迴應請求的校驗和
echoReq.icmpHdr.Checksum = checksum((u_short *)&echoReq, sizeof(ECHOREQUEST));


// 發送迴應請求    
nRet = sendto(s, // 建立起的套接字
(LPSTR)&echoReq, // 發送的緩衝區內容
sizeof(ECHOREQUEST),
0, // 標誌位
(struct sockaddr*)lpstToAddr, // 發送的目標地址
sizeof(SOCKADDR_IN));   // 地址結構長度
if (nRet == SOCKET_ERROR)
{
printf("sendto() error:%d\n",WSAGetLastError());
}
return (nRet);
}


// 接收應答回覆並進行解析
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL) 
{
ECHOREPLY echoReply;
int nRet;
int nAddrLen = sizeof(struct sockaddr_in);
//接收應答回覆
nRet = recvfrom(s, // 接收的套接字
(LPSTR)&echoReply, // 接收的緩衝區
sizeof(ECHOREPLY), // 緩衝區長度
0, // 標識
(LPSOCKADDR)lpsaFrom, // 接收的地址
&nAddrLen); // 地址結構長度


// 檢驗接收結果
if (nRet == SOCKET_ERROR) 
{
printf("recvfrom() error:%d\n",WSAGetLastError());
}
    // 記錄返回的TTL
*pTTL = echoReply.ipHdr.TTL;
//返回應答時間
return(echoReply.echoRequest.dwTime);  
}


// 等待迴應答覆,使用select機制
int WaitForEchoReply(SOCKET s)
{
struct 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));
}
// Ping功能實現
void Ping(char *pstrHost)
{
SOCKET  rawSocket;
LPHOSTENT lpHost;
struct    sockaddr_in destIP;
struct    sockaddr_in srcIP;
DWORD  dwTimeSent;
DWORD  dwElapsed;
u_char    cTTL;
int       nLoop;
int       nRet;


// 創建原始套接字,ICMP類型
rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (rawSocket == SOCKET_ERROR) 
{
printf("socket() error:%d\n",WSAGetLastError());
return;
}
// 檢測目標主機
lpHost = gethostbyname(pstrHost);
if (lpHost == NULL)
{
printf("Host not found: %s\n", pstrHost);
return;
}
// 設置目標機地址
destIP.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
//destIP.sin_addr.s_addr = inet_addr("127.0.0.1");
destIP.sin_family = AF_INET;
destIP.sin_port = 0;
// 提示開始進行Ping
printf("\nPinging %s with %d bytes of data:\n",
inet_ntoa(destIP.sin_addr),
REQ_DATASIZE);
// 發起多次Ping測試
for (nLoop = 0; nLoop < 4; nLoop++)
{
//發送ICMP迴應請求
SendEchoRequest(rawSocket, &destIP);
// 使用select()等待回覆的數據
nRet = WaitForEchoReply(rawSocket);
if (nRet == SOCKET_ERROR)
{
printf("select() error:%d\n",WSAGetLastError());
break;
}
if (!nRet)
{
printf("\nRequest time out");
break;
}
//接收回復
dwTimeSent = RecvEchoReply(rawSocket, &srcIP, &cTTL);
// 計算花費的時間
dwElapsed = GetTickCount() - dwTimeSent;
printf("\nReply from %s: bytes=%d time=%ldms TTL=%d", 
               inet_ntoa(srcIP.sin_addr), 
  REQ_DATASIZE,
               dwElapsed,
               cTTL);
}
printf("\n");
nRet = closesocket(rawSocket); // 關閉套接字,釋放資源
if (nRet == SOCKET_ERROR)
{
printf("closesocket() error:%d\n",WSAGetLastError());
}
}
void main(int argc, char **argv)
{
    WSADATA wsd;
    int nRet;
// 檢測輸入的參數 控制檯下
//     if (argc != 2)
//     {
// printf("\nUsage: ping hostname\n");
// return;
//     }
    // 初始化Winsock
if (WSAStartup(MAKEWORD(1,1), &wsd) != 0)
    {
        printf("加載Winsock失敗!\n");
    }
//開始Ping
// Ping(argv[1]);控制檯下
Ping("192.168.45.45");
// 釋放Winsock資源
    WSACleanup();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章