C++實現僅判斷 ip是否可訪問的 ping (win32/Linux)

C++實現簡易的 ping

ping 的實現

  • ping 是基於 ICMP 協議實現的,而 ICMP 協議又是基於 IP 協議實現的(ICMP作爲IP協議的數據部分傳輸)
  • ping 通過 ICMP 協議中的 type=8 和 code=0 來實現的,這個 type 和 code 的組合在 ICMP 協議中表示請求回顯。如果能正常回顯,那麼返回的 ICMP 協議包中的類型是類型0,表示回顯成功。
  • IP 協議的頭部一般爲 20 個字節

更多細節請看:
聊聊實現C++跨平臺ping函數及ICMP請求回顯數據包解析

demo

#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "WS2_32")

struct WindowsSocketLibInit
{
    WindowsSocketLibInit()
    {
        WSADATA wsaData;
        WORD sockVersion = MAKEWORD(2, 2);
        WSAStartup(sockVersion, &wsaData);
    }
    ~WindowsSocketLibInit()
    {
        WSACleanup();
    }
} INITSOCKETGLOBALVARIABLE;
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <ctime>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#endif

#include <string>
#include <limits>

short getChecksum(unsigned short *buff, unsigned size);
bool ping(std::string ip)
{
    static unsigned INDEX = 0;
    const unsigned IP_HEADER_LENGTH = 20;
    const unsigned FILL_LENGTH = 32;

    struct IcmpHdr
    {
        unsigned char icmpType;
        unsigned char icmpCode;
        unsigned short icmpChecksum;
        unsigned short icmpId;
        unsigned short icmpSequence;
    };

    int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

    int timeoutTick = 200;
    setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutTick, sizeof(timeoutTick));

    sockaddr_in des = { AF_INET, htons(0) };
    des.sin_addr.s_addr = inet_addr(ip.c_str());

    char buff[sizeof(IcmpHdr) + FILL_LENGTH] = { 0 };
    IcmpHdr *pIcmpHdr = (IcmpHdr *)(buff);

    unsigned short id = std::rand() % (std::numeric_limits<unsigned short>::max)();

    pIcmpHdr->icmpType = 8;
    pIcmpHdr->icmpCode = 0;
    pIcmpHdr->icmpId = id;
    pIcmpHdr->icmpSequence = INDEX++;
    std::memcpy(&buff[sizeof(IcmpHdr)], "TestTest", sizeof("TestTest"));
    pIcmpHdr->icmpChecksum = getChecksum((unsigned short *)buff, sizeof(buff));

	if (-1 == sendto(socketFd, buff, sizeof(buff), 0, (sockaddr *)&des, sizeof(des)))
	{
		return false;
	}
      
    char recv[1 << 10];
    int ret = recvfrom(socketFd, recv, sizeof(recv), 0, NULL, NULL);
    if (-1 == ret || ret < IP_HEADER_LENGTH + sizeof(IcmpHdr))
	{
		return false;
	}
        
    IcmpHdr *pRecv = (IcmpHdr *)(recv + IP_HEADER_LENGTH);
    return !(pRecv->icmpType != 0 || pRecv->icmpId != id);
}

short getChecksum(unsigned short *buff, unsigned size)
{
    unsigned long ret = 0;
    for (unsigned i = 0; i < size; i += sizeof(unsigned short)) 
    {
        ret += *buff++;
    }
    if (size & 1) 
    {
        ret += *(unsigned char *)buff;
    }
    ret = (ret >> 16) + (ret & 0xFFFF);
    ret += ret >> 16;

    return (unsigned short)~ret;
}

Test

#include <iostream>
#include"Ping.h"
using namespace std;

int main(int argc, char const *argv[])
{
	if (argc < 2)
	{
		/* code */
		cout << "Usage : ./main [ip]" << endl;;
		return 0;
	}
	string ip;
	ip = argv[1];
	cout << ping(ip) << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章