您的位置:首页 > 其它

通过ICMP协议测试目标主机是否在线

2014-11-28 16:31 211 查看
/*
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(原始套接字)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐