vc實現ping

林堯彬發表於2020-04-04
//ping.h
#ifndef _CPING_H_
#define _CPING_H_
#include <Winsock2.h>
#include <Windows.h>
#pragma pack(1)

#define ICMP_ECHOREPLY	0
#define ICMP_ECHOREQ	8
#define REQ_DATASIZE	32		// Echo 請求資料的大小

class CPing
{
public:
	//ping host, nRetries-ping次數
	bool Ping(LPCSTR pstrHost, UINT nRetries = 4);

	void Result(int* nElapseTime, float* fMissPack = NULL, u_char* cTTL = NULL);
	//void Result(CPing::REQ_RESULT& result);

private:
	int  WaitForEchoReply(SOCKET s);
	//ICMP回應的請求和回答函式
	int		SendEchoRequest(SOCKET, LPSOCKADDR_IN);
	DWORD	RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);
	u_short in_cksum(u_short *addr, int len);

private:
	struct REQ_RESULT
	{
		int			nElapseTime;	//請求響應時間。
		u_char		cTTL;			//請求TTL(生存時間)
		float		fMissPack;	//丟包率
	};

	REQ_RESULT m_Result;
};

// IP Header -- RFC 791
typedef struct tagIPHDR
{
	u_char  VIHL;			// Version and IHL
	u_char	TOS;			// Type Of Service
	short	TotLen;			// Total Length
	short	ID;				// Identification
	short	FlagOff;		// Flags and Fragment Offset
	u_char	TTL;			// Time To Live
	u_char	Protocol;		// Protocol
	u_short	Checksum;		// Checksum
	struct	in_addr iaSrc;	// Internet Address - Source
	struct	in_addr iaDst;	// Internet Address - Destination
}IPHDR, *PIPHDR;


// ICMP Header - RFC 792
typedef struct tagICMPHDR
{
	u_char	Type;			// Type
	u_char	Code;			// Code
	u_short	Checksum;		// Checksum
	u_short	ID;				// Identification
	u_short	Seq;			// Sequence
	char	Data;			// Data
}ICMPHDR, *PICMPHDR;



// ICMP Echo Request
typedef struct tagECHOREQUEST
{
	ICMPHDR icmpHdr;
	DWORD	dwTime;
	char	cData[REQ_DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;


// ICMP Echo Reply
typedef struct tagECHOREPLY
{
	IPHDR	ipHdr;
	ECHOREQUEST	echoRequest;
	char    cFiller[256];
}ECHOREPLY, *PECHOREPLY;

#pragma pack()

#endif

  

//ping.cpp
#include "ping.h"

#pragma comment(lib, "ws2_32.lib")

bool CPing::Ping(LPCSTR pstrHost, UINT nRetries)
{
	//建立一個Raw套節字
	SOCKET rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (rawSocket == INVALID_SOCKET) 
	{
		int err = WSAGetLastError();
		return false;
	}
	int nNetTimeout = 1000;//1秒 
	//傳送時限
	setsockopt(rawSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int));
	//接收時限
	setsockopt(rawSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));

	//獲得主機資訊
	LPHOSTENT lpHost = gethostbyname(pstrHost);
	if (lpHost == NULL)
	{
		return false;
	}
	
	//構造目標套節字地址資訊
	struct    sockaddr_in saDest;
	struct    sockaddr_in saSrc;
	saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
	saDest.sin_family = AF_INET;
	saDest.sin_port = 3077;//0;

	DWORD	 dwTimeSent;
	u_char   cTTL;
	int		nRet;
	int		nRecvNum = 0;
	int		nTotalTime = 0;

	//多次ping
	for (UINT nLoop = 0; nLoop < nRetries; ++nLoop)
	{
		//傳送ICMP回應請求
		if ((nRet = SendEchoRequest(rawSocket, &saDest)) < 0)
		{
			break;
		}

		if ((nRet = WaitForEchoReply(rawSocket)) == SOCKET_ERROR)
		{
			break;
		}
		if (nRet)
		{
			//獲得回應
			if ( (dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL)) < 0)
			{
				nRet = dwTimeSent;
				break;
			}
			//計算時間
			nTotalTime += GetTickCount() - dwTimeSent;
			//Sleep(1000);
			++nRecvNum;
		}
	}
	closesocket(rawSocket);
	if (nRecvNum > 0 && nRet >= 0)
	{
		m_Result.nElapseTime = nTotalTime/nRetries;
		m_Result.cTTL = cTTL;
		m_Result.fMissPack = (float)(nRetries - nRecvNum)/nRetries;
		return true;
	}

	return false;
}

//傳送ICMPECHO資料包請求
int CPing::SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr) 
{
	static ECHOREQUEST echoReq;
	static int nId = 1;
	static int nSeq = 1;
	int nRet;

	//構造回應請求
	echoReq.icmpHdr.Type		= ICMP_ECHOREQ;
	echoReq.icmpHdr.Code		= 0;
	echoReq.icmpHdr.Checksum	= 0;
	echoReq.icmpHdr.ID			= nId++;
	echoReq.icmpHdr.Seq			= nSeq++;

	for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
		echoReq.cData[nRet] = ' '+nRet;

	//儲存傳送時間
	echoReq.dwTime	= GetTickCount();

	echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));

	//傳送請求
	nRet = sendto(s,						
				 (LPSTR)&echoReq,			
				 sizeof(ECHOREQUEST),
				 0,							
				 (LPSOCKADDR)lpstToAddr, 
				 sizeof(SOCKADDR_IN));   
	//檢查返回值
	if (nRet == SOCKET_ERROR) 
	{
	}

	return (nRet);
}

//接收ICMPECHO資料包回應
DWORD CPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL) 
{
	ECHOREPLY echoReply;
	int nRet;
	int nAddrLen = sizeof(struct sockaddr_in);

	//接收請求回應
	nRet = recvfrom(s,					
					(LPSTR)&echoReply,	
					sizeof(ECHOREPLY),	
					0,					
					(LPSOCKADDR)lpsaFrom,
					&nAddrLen);			

	//檢查返回值
	if (nRet == SOCKET_ERROR) 
	{
		return nRet;
	}

	//返回傳送的時間
	*pTTL = echoReply.ipHdr.TTL;

	return(echoReply.echoRequest.dwTime);   		
}

//等待回應
int CPing::WaitForEchoReply(SOCKET s)
{
	struct timeval Timeout;
	fd_set readfds;

	readfds.fd_count = 1;
	readfds.fd_array[0] = s;
	Timeout.tv_sec = 1;
    Timeout.tv_usec = 0;

	return(select(1, &readfds, NULL, NULL, &Timeout));
}

//轉換地址
u_short CPing::in_cksum(u_short *addr, int len)
{
	register int nleft = len;
	register u_short *w = addr;
	register u_short answer;
	register int sum = 0;

	while( nleft > 1 )  {
		sum += *w++;
		nleft -= 2;
	}

	if( nleft == 1 ) {
		u_short	u = 0;

		*(u_char *)(&u) = *(u_char *)w ;
		sum += u;
	}

	sum = (sum >> 16) + (sum & 0xffff);	
	sum += (sum >> 16);			
	answer = ~sum;				
	return (answer);
}

void CPing::Result(int* nElapseTime, float* fMissPack, u_char* cTTL)
{
	if (nElapseTime)
	{
		*nElapseTime = m_Result.nElapseTime;
	}
	if (fMissPack)
	{
		*fMissPack = m_Result.fMissPack;
	}
	if (cTTL)
	{
		*cTTL = m_Result.cTTL;
	}
}

  

//main.cpp
#include <stdlib.h>
#include <iostream>
#include <string>
#include "ping.h"

int main(int argn, char *argv[])
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );

	CPing ping;
	bool bResult = false;
	if (argn == 2)
	{
		bResult = ping.Ping(argv[1]);
	}
	else
	{
		bResult = ping.Ping("www.baidu.com");
	}
	
	std::cout << "result : " << bResult << std::endl;
	if (bResult)
	{
		int nTime;
		u_char nTTL;
		float fMiss;
		ping.Result(&nTime, &fMiss, &nTTL);

		std::cout << "time : " << nTime << " TTL : " << (int)nTTL << " miss : " << fMiss*100 << "% " << std::endl;
	}
	return 0;
}

  

轉載於:https://www.cnblogs.com/chunxi/p/3754963.html

相關文章