Ping程序实现

OSI模型与ICP/IP模型对比:

Ping通过发送ICMP回显请求来确定过一个主机是否存在:

#include "stdafx.h"
#include "Winsock2.h"
#pragma comment ( lib, "ws2_32.lib" )

#include <windows.h>
#include "stdio.h"

// 定义默认的缓冲区长度
#define DEF_BUF_SIZE		1024
// 定义IP包头长度
#define IP_HEADER_SIZE		20
// 定义ICMP包头长度
#define ICMP_HEADER_SIZE	( sizeof(ICMP_HEADER) )
// 定义ICMP包实体数据长度
#define ICMP_DATA_SIZE		32
// 定义ICMP包总长度
#define ICMP_PACK_SIZE		( ICMP_HEADER_SIZE + ICMP_DATA_SIZE )
// 定义ICMP包头
typedef struct _ICMP_HEADER {
	BYTE	bType ;			// 类型
	BYTE	bCode ;			// 代码
	USHORT	nCheckSum ;		// 校验和
	USHORT	nId ;			// 标识,本进程ID
	USHORT	nSequence ;		// 序列号
	UINT	nTimeStamp ;	// 可选项,这里为时间,用于计算时间
} ICMP_HEADER, *PICMP_HEADER ;

// 用于格式化输出信息的缓冲区
char szInfo[DEF_BUF_SIZE] ;

BOOL Ping ( char* lpDestIp ) ;
USHORT GetCheckSum ( LPBYTE lpBuf, DWORD dwSize ) ;

int main(int argc, char* argv[])
{
	int value[4] ;
	char szDestIp[DEF_BUF_SIZE] = {0} ;
	while ( scanf ( "%s", szDestIp) )
		Ping ( szDestIp ) ;
	return 0;
}

// 实现PING程序
// 参数:目标地址IP字符串,格式如“127.0.0.1”
BOOL Ping ( char* lpDestIp )
{
	// 设置目标地址
	SOCKADDR_IN DestSockAddr ;
	DestSockAddr.sin_family = AF_INET ;
	DestSockAddr.sin_addr.s_addr = inet_addr( lpDestIp ) ;
	DestSockAddr.sin_port = htons ( 0 ) ;

	// 创建ICMP回显请求包
	char ICMPPack[ICMP_PACK_SIZE] = {0} ;
	PICMP_HEADER pICMPHeader = (PICMP_HEADER)ICMPPack ;
	pICMPHeader->bType = 8 ;
	pICMPHeader->bCode = 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 ( RawSock == INVALID_SOCKET )
	{
		printf ( "Create socket error!\n" ) ;
		return FALSE ;
	}
	
	// 设置接收超时为1秒
	int nTime = 1000 ;
	int ret = ::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 ;				// 初始时校验值为0
		pICMPHeader->nSequence = i ;				// 序号
		pICMPHeader->nTimeStamp = ::GetTickCount() ;// 当前时间

		// 计算校验值,校验值要在ICMP数据报创建完才能计算
		pICMPHeader->nCheckSum = GetCheckSum ( (LPBYTE)ICMPPack, ICMP_PACK_SIZE ) ;
		
		// 发送ICMP数据包
		int nRet = ::sendto ( RawSock, ICMPPack, ICMP_PACK_SIZE,0,(SOCKADDR*)&DestSockAddr, sizeof(DestSockAddr) ) ;
		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 ;
			}
		}

		// 计算ICMP数据报的时间差
		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->bType != 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 ;
}

// 计算ICMP包校验值
// 参数1:ICMP包缓冲区
// 参数2:ICMP包长度
USHORT GetCheckSum ( LPBYTE lpBuf, DWORD dwSize )
{
	DWORD	dwCheckSum = 0 ;
	USHORT* lpWord = (USHORT*)lpBuf ;

	// 累加
	while ( dwSize > 1 )
	{
		dwCheckSum += *lpWord++ ;
		dwSize -= 2 ;
	}

	// 如果长度是奇数
	if ( dwSize == 1 )
		dwCheckSum += *((LPBYTE)lpWord) ;

	// 高16位和低16位相加
	dwCheckSum = ( dwCheckSum >> 16 ) + ( dwCheckSum & 0xFFFF ) ;

	// 取反
	return (USHORT)(~dwCheckSum ) ;
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值