【TCP/IP】C语言实现Ping小程序

 Ping程序一般用来测试一台主机是否可达,该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显 应答。

一般来说,如果不能Ping到某主机,那么就不能Telnet或者FTP到那台主机。反过来,如果不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。Ping程序还可以检测出到这台主机的往返时间,以表明该主机里我们有“多远”。大多数的TCP/IP实现都在内核中直接支持Ping服务器。

 

ICMP回显请求和回显应答报文如下所示

 

复制代码
1 /****************************************************************/
2 /*        类型(0或8)|        代码(0)|            校验和        |*/
3 /****************************************************************/
4 /*                标识符            |                序号        |*/
5 /****************************************************************/
6 /*                            选项数据                           |*/
7 /****************************************************************/
复制代码

 

定义ICMP报头数据结构

 

复制代码
1 typedef struct _ICMP_HEADER{
2     BYTE nType;
3     BYTE nCode;
4     USHORT nCheckSum;
5     USHORT nId;
6     USHORT nSequence;
7     UINT nTimeStamp;
8 }ICMP_HEADER,*PICMP_HEADER;
9  
复制代码

 

下面使用Socket实现Ping小程序。

复制代码
  1 // PingSock.cpp : 定义控制台应用程序的入口点。
  2 //
  3 #include "stdafx.h"
  4 #include <WinSock2.h>
  5 #pragma comment(lib,"ws2_32.lib")
  6 #include <Windows.h>
  7 
  8 //定义默认缓冲区长度
  9 #define DEF_BUF_SIZE 1024
 10 #define IP_HEADER_SIZE 20
 11 #define ICMP_HEADER_SIZE (sizeof(ICMP_HEADER))
 12 #define ICMP_DATA_SIZE 32
 13 #define ICMP_PACK_SIZE (ICMP_HEADER_SIZE + ICMP_DATA_SIZE)
 14 
 15 typedef struct _ICMP_HEADER{
 16     BYTE nType;
 17     BYTE nCode;
 18     USHORT nCheckSum;
 19     USHORT nId;
 20     USHORT nSequence;
 21     UINT nTimeStamp;
 22 }ICMP_HEADER,*PICMP_HEADER;
 23  
 24 char szInfo[DEF_BUF_SIZE] = {0};
 25 
 26 USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize);
 27 BOOL Ping(char* lpDestIp);
 28 
 29 int _tmain(int argc, _TCHAR* argv[])
 30 {
 31     char szDestIp[DEF_BUF_SIZE] = {0} ;
 32     while ( scanf ( "%s", szDestIp) )
 33         Ping ( szDestIp ) ;
 34      
 35     return 0;
 36 }
 37 
 38 USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize)
 39 {
 40     DWORD dwCheckSum = 0;
 41     USHORT* lpWord = (USHORT*)lpBuf;
 42 
 43     while( dwSize > 1)
 44     {
 45         dwCheckSum += *lpWord++;
 46         dwSize -= 2;
 47     }
 48 
 49     if(1 == dwSize)
 50         dwCheckSum += *((USHORT*)lpBuf);
 51 
 52     dwCheckSum = ( dwCheckSum >> 16) + ( dwCheckSum & 0xffff);
 53     return (USHORT)(~dwCheckSum);
 54 }
 55 
 56 BOOL Ping(char* lpDestIp)
 57 {
 58     SOCKADDR_IN DestAddr;
 59     DestAddr.sin_family = AF_INET;
 60     DestAddr.sin_addr.S_un.S_addr = inet_addr(lpDestIp);
 61     DestAddr.sin_port = htons(0);
 62 
 63     //创建ICMP请求包
 64     char ICMPPack[ICMP_PACK_SIZE] = {0};
 65     PICMP_HEADER pICMPHeader = (PICMP_HEADER)ICMPPack;
 66     pICMPHeader->nType = 8;
 67     pICMPHeader->nCode = 0;
 68     pICMPHeader->nId = (USHORT)::GetCurrentProcessId();
 69     pICMPHeader->nCheckSum = 0;
 70     pICMPHeader->nTimeStamp = 0;
 71     memset(&(ICMPPack[ICMP_HEADER_SIZE]),'E',ICMP_DATA_SIZE);
 72 
 73     //初始化WinSock
 74     WORD wVersionRequested = MAKEWORD(2,2);
 75     WSADATA wsaData;
 76     if(WSAStartup(wVersionRequested,&wsaData) != 0)
 77     {
 78         return FALSE;
 79     }
 80 
 81     //创建初始套接字
 82     SOCKET RawSock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
 83     if(INVALID_SOCKET == RawSock)
 84     {
 85         printf("create raw socket error\n");
 86         return FALSE;
 87     }
 88 
 89     int nTime = 1000;
 90     int nRet = ::setsockopt( RawSock, SOL_SOCKET, SO_RCVTIMEO,(char*)(&nTime),sizeof(nTime));
 91 
 92     char szRecvBuf [ DEF_BUF_SIZE] ;
 93     SOCKADDR_IN    SourSockAddr ;
 94 
 95     for(int i = 0; i < 4; i++)
 96     {
 97         pICMPHeader->nCheckSum = 0;
 98         pICMPHeader->nTimeStamp = ::GetTickCount();
 99         pICMPHeader->nSequence = i;
100 
101         pICMPHeader->nCheckSum = GetCheckSum ( (LPBYTE)ICMPPack, ICMP_PACK_SIZE ) ;
102 
103         int nRet = ::sendto( RawSock, ICMPPack, ICMP_PACK_SIZE, 0, (SOCKADDR*)&DestAddr, sizeof(DestAddr));
104         if ( nRet == SOCKET_ERROR )
105         {
106             printf ( "sendto error!\n" ) ;
107             return FALSE ;
108         }
109 
110         // 接收ICMP响应
111         int nLen = sizeof(SourSockAddr) ;
112         nRet = ::recvfrom ( RawSock, szRecvBuf, DEF_BUF_SIZE,0,(SOCKADDR*)&SourSockAddr, &nLen ) ;
113         if ( nRet == SOCKET_ERROR )
114         {
115             if ( ::WSAGetLastError() == WSAETIMEDOUT )
116             {
117                 printf ( "Request Timeout\n" ) ;
118                 continue ;
119             }
120             else
121             {
122                 printf ( "recvfrom error!\n" ) ;
123                 return FALSE ;
124             }
125         }
126 
127         int nTime = ::GetTickCount() - pICMPHeader->nTimeStamp ;
128 
129         int nRealSize = nRet - IP_HEADER_SIZE - ICMP_HEADER_SIZE ;
130         if ( nRealSize < 0  )
131         {
132             printf ( "To less recv bytes!\n" ) ;
133             continue ;
134         }
135 
136         // 检测是否当前所发出的ICMP响应包
137         PICMP_HEADER pRecvHeader = (PICMP_HEADER)(szRecvBuf+IP_HEADER_SIZE) ;
138         if ( pRecvHeader->nType != 0 )
139         {
140             printf ( "Not ICMP respond type!\n" ) ;
141             return FALSE ;
142         }
143 
144         if ( pRecvHeader->nId != ::GetCurrentProcessId () )
145         {
146             printf ( "not valid id!\n" ) ;
147             return FALSE ;
148         }
149 
150         printf ( "%d bytes replay from %s : bytes=%d time=%dms\n", \
151             nRet, inet_ntoa(SourSockAddr.sin_addr), nRealSize, nTime ) ;
152 
153         ::Sleep ( 1000 ) ;
154     }
155 
156     closesocket ( RawSock ) ;
157     WSACleanup () ;
158 
159     return TRUE ;
160 }
posted @ 2012-12-23 17:03  梓涵VV  阅读(812)  评论(0)    收藏  举报