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;
}