您的位置:首页 > 理论基础 > 计算机网络

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

2012-12-23 17:03 1011 查看
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 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: