/* AliveChecker.h author: zhouciming@163.com */ #ifndef __ALIVE_CHECKER_H__ #define __ALIVE_CHECKER_H__ typedef unsigned short ushort; typedef unsigned long ulong; class CPing { public: CPing(); ~CPing(); bool ping(ulong destIP, int timeout = 10000); protected: bool init(); ushort getCheckSum(ushort* addr, int len); // 计算校验和 private: int m_sockfd; }; #endif
/* AliveChecker.cpp author: zhouciming@163.com */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/time.h> #include <arpa/inet.h> #include <netinet/ip_icmp.h> #include "AliveChecker.h" #define INVALID_SOCKET -1 #define PACKET_SIZE 1024 CPing::CPing() { m_sockfd = INVALID_SOCKET; } CPing::~CPing() { if(m_sockfd != INVALID_SOCKET) close(m_sockfd); } bool CPing::init() // 初始化socket fd { if(m_sockfd != INVALID_SOCKET) close(m_sockfd); m_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (m_sockfd < 0) { perror("socket"); return false; } return true; } // 向目标地址发送一个icmp数据包, 并接收响应来判断目标主机是否在线 bool CPing::ping(ulong destIP, int time_out) // time_out: 超时时间(单位: 毫秒) { if(m_sockfd == INVALID_SOCKET && init() == false) return false; struct timeval timeout; timeout.tv_sec = time_out / 1000; // 秒 timeout.tv_usec = (time_out % 1000) * 1000; // 微秒 if (setsockopt(m_sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt"); return false; } struct icmp* icmp; struct timeval *tval; char sendpacket[PACKET_SIZE] = {0}; pid_t pid = getpid(); icmp = (struct icmp*)sendpacket; icmp->icmp_type = ICMP_ECHO; //回显请求 icmp->icmp_code = 0; icmp->icmp_cksum = 0; icmp->icmp_seq = 0; icmp->icmp_id = pid; tval = (struct timeval*)icmp->icmp_data; gettimeofday(tval, NULL); // 获取当前时间 icmp->icmp_cksum = getCheckSum((ushort*)icmp, sizeof(struct icmp)); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = destIP; // 设置目标地址 // 发送ICMP数据包 int n = sendto(m_sockfd, (char*)&sendpacket, sizeof(struct icmp), 0, (struct sockaddr*)&addr, sizeof(addr)); if (n < 1) { return false; } while(1) // 可能会接收到其他ping的应答消息, 所以这里用循环 { int maxfds; fd_set readfds; struct sockaddr_in from; int fromlen = sizeof(from); char recvBuf[PACKET_SIZE] = {0}; FD_ZERO(&readfds); FD_SET(m_sockfd, &readfds); maxfds = m_sockfd + 1; n = select(maxfds, &readfds, NULL, NULL, &timeout); if (n <= 0) { return false; } n = recvfrom(m_sockfd, recvBuf, sizeof(recvBuf), 0, (struct sockaddr *)&from, (socklen_t *)&fromlen); if (n < 1) { break; } ulong fromIP = from.sin_addr.s_addr; if (fromIP != destIP) // 不是目标主机的echo信息 { return false; } struct ip* iph = (struct ip *)recvBuf; icmp = (struct icmp *)(recvBuf + (iph->ip_hl << 2)); if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid) // ICMP_ECHOREPLY回显应答 { return true; } } return false; } ushort CPing::getCheckSum(ushort* addr, int len) { int sum = 0; ushort *w = addr; ushort answer = 0; while(len > 1) { sum += *w++; len -= 2; } if( len == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; }
/* main.cpp author: zhouciming@163.com */ #include <stdio.h> #include <arpa/inet.h> #include "AliveChecker.h" void test(const char* ip) { CPing ping; in_addr_t destIP = inet_addr(ip); if(ping.ping(destIP)) { printf("ping successful\n"); } else { printf("ping failed\n"); } } int main(int argc, char* argv[]) { if(argc < 2) { printf("Usage: %s <destIP>\n", argv[0]); return 1; } else { const char* ip = argv[1]; test(ip); } return 0; }
Makefile:
#CROSS = arm-hisiv100nptl-linux- CXX = $(CROSS)g++ RM=rm -f CFLAGS = -Wall -Os -DLINUX ALL=a all: $(ALL) a: main.cpp AliveChecker.cpp $(CXX) $(CFLAGS) -o $@ $^ clean: $(RM) *.o $(ALL)
注意:以上编译出来的程序必须以root用户执行,因为ICMP协议用到了SOCK_RAW(原始套接字)。