【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 }


浙公网安备 33010602011771号