Ping程序一般用来测试一台主机是否可达,该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显 应答。
一般来说,如果不能Ping到某主机,那么就不能Telnet或者FTP到那台主机。反过来,如果不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。Ping程序还可以检测出到这台主机的往返时间,以表明该主机里我们有“多远”。大多数的TCP/IP实现都在内核中直接支持Ping服务器。
ICMP回显请求和回显应答报文如下所示
/****************************************************************/
/* 类型(0或8)| 代码(0)| 校验和 |*/
/****************************************************************/
/* 标识符 | 序号 |*/
/****************************************************************/
/* 选项数据 |*/
/****************************************************************/
定义ICMP报头数据结构
typedef struct _ICMP_HEADER{
BYTE nType;
BYTE nCode;
USHORT nCheckSum;
USHORT nId;
USHORT nSequence;
UINT nTimeStamp;
}ICMP_HEADER,*PICMP_HEADER;
下面使用Socket实现Ping小程序。
// PingSock.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h>
//定义默认缓冲区长度
#define DEF_BUF_SIZE 1024
#define IP_HEADER_SIZE 20
#define ICMP_HEADER_SIZE (sizeof(ICMP_HEADER))
#define ICMP_DATA_SIZE 32
#define ICMP_PACK_SIZE (ICMP_HEADER_SIZE + ICMP_DATA_SIZE)
typedef struct _ICMP_HEADER{
BYTE nType;
BYTE nCode;
USHORT nCheckSum;
USHORT nId;
USHORT nSequence;
UINT nTimeStamp;
}ICMP_HEADER,*PICMP_HEADER;
char szInfo[DEF_BUF_SIZE] = {0};
USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize);
BOOL Ping(char* lpDestIp);
int _tmain(int argc, _TCHAR* argv[])
{
char szDestIp[DEF_BUF_SIZE] = {0} ;
while ( scanf ( "%s", szDestIp) )
Ping ( szDestIp ) ;
return 0;
}
USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize)
{
DWORD dwCheckSum = 0;
USHORT* lpWord = (USHORT*)lpBuf;
while( dwSize > 1)
{
dwCheckSum += *lpWord++;
dwSize -= 2;
}
if(1 == dwSize)
dwCheckSum += *((USHORT*)lpBuf);
dwCheckSum = ( dwCheckSum >> 16) + ( dwCheckSum & 0xffff);
return (USHORT)(~dwCheckSum);
}
BOOL Ping(char* lpDestIp)
{
SOCKADDR_IN DestAddr;
DestAddr.sin_family = AF_INET;
DestAddr.sin_addr.S_un.S_addr = inet_addr(lpDestIp);
DestAddr.sin_port = htons(0);
//创建ICMP请求包
char ICMPPack[ICMP_PACK_SIZE] = {0};
PICMP_HEADER pICMPHeader = (PICMP_HEADER)ICMPPack;
pICMPHeader->nType = 8;
pICMPHeader->nCode = 0;
pICMPHeader->nId = (USHORT)::GetCurrentProcessId();
pICMPHeader->nCheckSum = 0;
pICMPHeader->nTimeStamp = 0;
memset(&(ICMPPack[ICMP_HEADER_SIZE]),'E',ICMP_DATA_SIZE);
//初始化WinSock
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(wVersionRequested,&wsaData) != 0)
{
return FALSE;
}
//创建初始套接字
SOCKET RawSock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
if(INVALID_SOCKET == RawSock)
{
printf("create raw socket error\n");
return FALSE;
}
int nTime = 1000;
int nRet = ::setsockopt( RawSock, SOL_SOCKET, SO_RCVTIMEO,(char*)(&nTime),sizeof(nTime));
char szRecvBuf [ DEF_BUF_SIZE] ;
SOCKADDR_IN SourSockAddr ;
for(int i = 0; i < 4; i++)
{
pICMPHeader->nCheckSum = 0;
pICMPHeader->nTimeStamp = ::GetTickCount();
pICMPHeader->nSequence = i;
pICMPHeader->nCheckSum = GetCheckSum ( (LPBYTE)ICMPPack, ICMP_PACK_SIZE ) ;
int nRet = ::sendto( RawSock, ICMPPack, ICMP_PACK_SIZE, 0, (SOCKADDR*)&DestAddr, sizeof(DestAddr));
if ( nRet == SOCKET_ERROR )
{
printf ( "sendto error!\n" ) ;
return FALSE ;
}
// 接收ICMP响应
int nLen = sizeof(SourSockAddr) ;
nRet = ::recvfrom ( RawSock, szRecvBuf, DEF_BUF_SIZE,0,(SOCKADDR*)&SourSockAddr, &nLen ) ;
if ( nRet == SOCKET_ERROR )
{
if ( ::WSAGetLastError() == WSAETIMEDOUT )
{
printf ( "Request Timeout\n" ) ;
continue ;
}
else
{
printf ( "recvfrom error!\n" ) ;
return FALSE ;
}
}
int nTime = ::GetTickCount() - pICMPHeader->nTimeStamp ;
int nRealSize = nRet - IP_HEADER_SIZE - ICMP_HEADER_SIZE ;
if ( nRealSize < 0 )
{
printf ( "To less recv bytes!\n" ) ;
continue ;
}
// 检测是否当前所发出的ICMP响应包
PICMP_HEADER pRecvHeader = (PICMP_HEADER)(szRecvBuf+IP_HEADER_SIZE) ;
if ( pRecvHeader->nType != 0 )
{
printf ( "Not ICMP respond type!\n" ) ;
return FALSE ;
}
if ( pRecvHeader->nId != ::GetCurrentProcessId () )
{
printf ( "not valid id!\n" ) ;
return FALSE ;
}
printf ( "%d bytes replay from %s : bytes=%d time=%dms\n", \
nRet, inet_ntoa(SourSockAddr.sin_addr), nRealSize, nTime ) ;
::Sleep ( 1000 ) ;
}
closesocket ( RawSock ) ;
WSACleanup () ;
return TRUE ;
}