1. 程式人生 > >基於ICMP的Ping IPV6化 (IPV4&IPV6)

基於ICMP的Ping IPV6化 (IPV4&IPV6)

最近在做一個有關網路程式設計的專案,本來對網路方面的知識沒有很深的瞭解,在開發過程中遇到了很多的困難,最終一步一個腳印,也算是守的雲開見月明。在這個開個貼記錄下點點滴滴,以後再補充,為自己也為他人今後參考之用。以下是針對IPV4、IPV6的Ping程式,不多說先貼上核心程式碼(loadRunner部分的程式碼已經注掉)。

BOOL	CICMPPing::Ping(std::string& Host, UINT reqcount, UINT reqcount_suc, UINT timeout, UINT interval, UINT pcksize)
{
	//定義destinationAddress資訊
	struct addrinfo *saDest;
	const char* destStr = "";
	bool isIPV6;
	u_long ulIP;
	int num = 0;
	WSADATA wsaData;
	int iResult;

	if (!m_initflg){
		lr_error_message("Initialization failed.");
		return FALSE;
	}
	//ホストが空かどうか
	if (Host.empty()){
		lr_error_message("Parameter Error.Host:%s", Host.c_str());
		return FALSE;
	}

	if (reqcount_suc > reqcount){
		lr_error_message("Parameter Error.Request count:%d Success count:%d.", reqcount, reqcount_suc);
		return FALSE;
	}

	std::vector<BYTE> buffer(pcksize);
	std::fill(buffer.begin(), buffer.end(), 'A');

	struct sockaddr_in6 addr6;
	// Initialize Winsock  在getaddrinfo呼叫前必須先呼叫WSAStartup()
	//為了在應用程式當中呼叫任何一個Winsock API函式,首先第一件事情就是必須通過WSAStartup函式完成對Winsock服務的初始化,因此需要呼叫WSAStartup函式。使用Socket的程式在使用Socket之前必須呼叫WSAStartup函式。
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		lr_error_message("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	//定義destinationAddress資訊
	destStr = Host.c_str();
	//UINT address = inet_addr(Host.c_str());
	
	//獲得目的地址判斷是否為IPV6(getaddrinfo是協議無關的函式)
	//getaddrinfo函式能夠處理名字到地址以及服務到埠這兩 種轉換,返回的是一個sockaddr結構的連結串列而不是一個地址清單。現在response的資訊都儲存在saDest裡面
	if ((num = getaddrinfo(destStr, 0, NULL, &saDest)) == 0)
	{
		if (isIPV6 = (saDest->ai_family == AF_INET6))
		{
		//	memcpy(&addr6, (sockaddr_in6*)(saDest->ai_addr), sizeof(sockaddr_in6));
			addr6 = *(sockaddr_in6*)(saDest->ai_addr);//進行一個強制轉換,為後面的m_pfunc_Icmp6SendEcho2函式準備引數
		}
		else
		{
			ulIP = ((sockaddr_in*)(saDest->ai_addr))->sin_addr.s_addr;
		}	
	}
//	lr_log_message("Host name: %s\n", inet_ntoa(*addr6);
	int ss = WSAGetLastError();

	//定義sourceAddress資訊
	struct sockaddr_in6 sa6Source;
	if (isIPV6){
		sa6Source.sin6_family = AF_INET6;
		sa6Source.sin6_flowinfo = 0;
		sa6Source.sin6_port = 0;
		sa6Source.sin6_scope_id = 0;      
		sa6Source.sin6_addr = in6addr_any;
	//	memset(sa6Source.sin6_addr.s6_addr, 0, sizeof(sa6Source.sin6_addr.s6_addr));
	//	*(sa6Source.sin6_addr.s6_addr + 15) = 1;
	}

	//傳送ICMP報文
	HANDLE hIP = (isIPV6 ? m_pfunc_Icmp6CreateFile() : m_pfunc_IcmpCreateFile());
	if (hIP == INVALID_HANDLE_VALUE) {
		lr_error_message("create icmp failed:%d\n", WSAGetLastError());
		return false;
	}


	PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc(GHND, sizeof(IP_ECHO_REPLY) + buffer.size());
	if (pIpe == NULL) {
		m_pfunc_IcmpCloseHandle(hIP);
		return FALSE;
	}

	pIpe->Data = &buffer[0];
	pIpe->DataSize = buffer.size();


	UINT LostPacketsCount = 0;
	DWORD dwStatus;
	IP_OPTION_INFORMATION ipInfo = { 255, 0, 0, 0, NULL };

	//針對IPV6的ReplyBuffer引數的設定參考MSDN,儘可能的大些,否則m_pfunc_Icmp6SendEcho2()可能返回 0
	char EchoRequest[64] = { 0 }, EchoReply[64 + sizeof(IP_ECHO_REPLY) + 8];

	for (UINT i(0); i<reqcount; i++) {
		if (isIPV6)
		{
			//傳送request請求
			dwStatus = m_pfunc_Icmp6SendEcho2(hIP,
				NULL,
				NULL,
				NULL,
				&(sa6Source),
				&addr6,
				EchoRequest, 
				sizeof(EchoRequest),
				&ipInfo,
				EchoReply,
				sizeof(EchoReply),
				timeout);
			DWORD errorNum = GetLastError();
		}
		else
		{
			//如果是IPV4
			dwStatus = m_pfunc_IcmpSendEcho(hIP,
				ulIP,
				&buffer[0], 
				buffer.size(), 
				NULL, 
				pIpe,
				sizeof(IP_ECHO_REPLY) + buffer.size(), 
				timeout);
		}

		if (dwStatus <= 0){
			lr_error_message("dwStatus <= 0 %d", dwStatus);
			if (pIpe->Status || (pIpe->DataSize != buffer.size())){
				LostPacketsCount++;
			}
		}
		if (dwStatus == 0 || pIpe->DataSize != pcksize) {
			lr_error_message("dwStatus: %d", dwStatus);
			lr_error_message("IcmpSendEcho returned error: %ld", GetLastError());
			LostPacketsCount++;
			lr_error_message("(%d)sent bytes:[%d], received bytes[%d]. packets:%d", i + 1, buffer.size(), pIpe->DataSize, dwStatus);

		}
		else {
			lr_log_message("(%d)sent bytes:[%d], received bytes[%d]. packets:%d", i + 1, buffer.size(), pIpe->DataSize, dwStatus);
		}

		Sleep(interval);
	}

	GlobalFree(pIpe);

	m_pfunc_IcmpCloseHandle(hIP);
	freeaddrinfo(saDest);

	//ロストパケット數が許容範囲を超えるとエラー
	if (LostPacketsCount > (reqcount - reqcount_suc)){
		lr_error_message("Connection error.Packets: Sent = %d, Received = %d, Lost = %d <%ld%% loss>", reqcount,
			reqcount - LostPacketsCount, LostPacketsCount, (int)(100 / (float)reqcount)*LostPacketsCount);
		return FALSE;
	}
	lr_log_message("IcmpSendEcho OK.Packets: Sent = %d, Received = %d, Lost = %d <%ld%% loss>", reqcount,
		reqcount - LostPacketsCount, LostPacketsCount, (int)(100 / (float)reqcount)*LostPacketsCount);

	return TRUE;
}