您的位置:首页 > 编程语言 > C语言/C++

c++ ping 功能实现

2018-02-11 11:19 441 查看
#if !defined(COMMON_FDSET_H)
#define COMMON_FDSET_H

#include <cassert>
#include <errno.h>
#include <algorithm>

#ifdef WIN32
//#include <winsock2.h>
#include <WS2TCPIP.H>
#include <stdlib.h>
#include <io.h>
#endif

#ifndef WIN32
typedef int SOCKET;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR   (-1)
#else
//typedef SOCKET SOCKET;
#endif

int closeSocket( SOCKET fd );

class FdSet
{
public:
FdSet() : size(0), numReady(0)
{
FD_ZERO(&read);
FD_ZERO(&write);
FD_ZERO(&except);
}

int select(struct timeval& tv)
{
return numReady = ::select(size, &read, &write, &except, &tv);
}

int selectMilliSeconds(unsigned long ms)
{
struct timeval tv;
tv.tv_sec = (ms/1000);
tv.tv_usec = (ms%1000)*1000;
return select(tv);
}

bool readyToRead(SOCKET fd)
{
return (FD_ISSET(fd, &read) != 0);
}

bool readyToWrite(SOCKET fd)
{
return (FD_ISSET(fd, &write) != 0);
}

bool hasException(SOCKET fd)
{
return (FD_ISSET(fd,&except) != 0);
}

void setRead(SOCKET fd)
{
assert( FD_SETSIZE >= 8 );
FD_SET(fd, &read);
size = ( int(fd+1) > size) ? int(fd+1) : size;
}

void setWrite(SOCKET fd)
{
FD_SET(fd, &write);
size = ( int(fd+1) > size) ? int(fd+1) : size;
}

void setExcept(SOCKET fd)
{
FD_SET(fd,&except);
size = ( int(fd+1) > size) ? int(fd+1) : size;
}

void clear(SOCKET fd)
{
FD_CLR(fd, &read);
FD_CLR(fd, &write);
FD_CLR(fd, &except);
}

void reset()
{
size = 0;
numReady = 0;
FD_ZERO(&read);
FD_ZERO(&write);
FD_ZERO(&except);
}

// Make this stuff public for async dns/ares to use
fd_set read;
fd_set write;
fd_set except;
int size;
int numReady;  // set after each select call
};

#endif

#include <assert.h>
#include <fcntl.h>

#ifndef WIN32
#include <errno.h>
#include <unistd.h>
#endif

#include "FdSet.h"

using namespace std;

#ifdef WIN32

typedef int socklen_t;

#define EWOULDBLOCK             WSAEWOULDBLOCK
#define EINPROGRESS             WSAEINPROGRESS
#define EALREADY                WSAEALREADY
#define ENOTSOCK                WSAENOTSOCK
#define EDESTADDRREQ            WSAEDESTADDRREQ
#define EMSGSIZE                WSAEMSGSIZE
#define EPROTOTYPE              WSAEPROTOTYPE
#define ENOPROTOOPT             WSAENOPROTOOPT
#define EPROTONOSUPPORT         WSAEPROTONOSUPPORT
#define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT
#define EOPNOTSUPP              WSAEOPNOTSUPP
#define EPFNOSUPPORT            WSAEPFNOSUPPORT
#define EAFNOSUPPORT            WSAEAFNOSUPPORT
#define EADDRINUSE              WSAEADDRINUSE
#define EADDRNOTAVAIL           WSAEADDRNOTAVAIL
#define ENETDOWN                WSAENETDOWN
#define ENETUNREACH             WSAENETUNREACH
#define ENETRESET               WSAENETRESET
#define ECONNABORTED            WSAECONNABORTED
#define ECONNRESET              WSAECONNRESET
#define ENOBUFS                 WSAENOBUFS
#define EISCONN                 WSAEISCONN
#define ENOTCONN                WSAENOTCONN
#define ESHUTDOWN               WSAESHUTDOWN
#define ETOOMANYREFS            WSAETOOMANYREFS
#define ETIMEDOUT               WSAETIMEDOUT
#define ECONNREFUSED            WSAECONNREFUSED
#define ELOOP                   WSAELOOP
#define EHOSTDOWN               WSAEHOSTDOWN
#define EHOSTUNREACH            WSAEHOSTUNREACH
#define EPROCLIM                WSAEPROCLIM
#define EUSERS                  WSAEUSERS
#define EDQUOT                  WSAEDQUOT
#define ESTALE                  WSAESTALE
#define EREMOTE                 WSAEREMOTE

#else
#define WSANOTINITIALISED  EPROTONOSUPPORT
#endif

int closeSocket( SOCKET fd )
{
#if defined(WIN32)
return closesocket(fd);
#else
int ret = ::close(fd);
return ret;
#endif
}
#ifndef __SocketPing_H__
#define __SocketPing_H__

#include "FdSet.h"

#define ICMP_ECHO       8
#define ICMP_ECHOREPLY  0

#define ICMP_MIN        8 // minimum 8 byte icmp packet (just header)
#define ICMP_MAGIC      2010

#define STATUS_FAILED   0xFFFF
#define DEF_PACKET_SIZE 32
#define MAX_PACKET      1024

/* The IP header */
typedef struct ip_hdr
{
unsigned int   h_len:4;         // length of the header
unsigned int   version:4;       // Version of IP
unsigned char  tos;             // Type of service
unsigned short total_len;       // total length of the packet
unsigned short ident;           // unique identifier
unsigned short frag_and_flags;  // flags
unsigned char  ttl;
unsigned char  proto;           // protocol (TCP, UDP etc)
unsigned short checksum;        // IP checksum
unsigned int   sourceIP;
unsigned int   destIP;
}IpHeader;

//
// ICMP header
//
typedef struct _ihdr
{
BYTE i_type;
BYTE i_code; /* type sub code */
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
/* This is not the std header, but we reserve space for time */
ULONG timestamp;
}IcmpHeader;

//IP option header--use with socket option IP_OPTIONS
typedef struct _ipoptionhdr
{
unsigned char code;     // Option type
unsigned char len;      // Length of option hdr
unsigned char ptr;      // Offset into optons
unsigned long addr[9];  // List of IP addrs
} IpOptionHeader;

// ping
bool Ping(const char * ip, int try_count = 4, int timeout = 3000);

#endif /*__SocketPing_H__*/
#include "SocketPing.h"

/*lint -e830*/
static USHORT MakeIcmpID()
{
static CMutex   g_mutex;
CGuard   guard(&g_mutex);
static USHORT iIcmpID = 1;
return iIcmpID++;
}

static USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;

while(size >1)
{
cksum += *buffer++;
size  -= sizeof(USHORT);
}

if(size )
{
cksum += *(UCHAR*)buffer;
}

cksum  = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}

/*
Helper function to fill in various stuff in our ICMP request.
*/
static void fill_icmp_data(char * icmp_data, int datasize, USHORT icmp_id)
{
IcmpHeader *icmp_hdr;
char *datapart;

icmp_hdr = (IcmpHeader*)icmp_data;

icmp_hdr->i_type  = ICMP_ECHO;
icmp_hdr->i_code  = 0;
icmp_hdr->i_id    = icmp_id;//ICMP_MAGIC;
icmp_hdr->i_cksum = 0;
icmp_hdr->i_seq   = 0;

datapart = icmp_data + sizeof(IcmpHeader);
//
// Place some junk in the buffer.
//
memset(datapart,'E', datasize - sizeof(IcmpHeader));
}

/*
The response is an IP packet. We must decode the IP header to locate
the ICMP data
//成功返回0;失败返回-1;如果收到别人回复返回1
*/
static int decode_resp(char *buf, int bytes,struct sockaddr_in *from, const char * ip, USHORT icmp_id)
{
IpHeader * iphdr = (IpHeader *)buf;

unsigned short iphdrlen = (unsigned short)iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes

if (bytes < iphdrlen + ICMP_MIN)
{
SOCKETDEV_ERROR("Ping [%s] too few bytes", ip);
return -1;
}

IcmpHeader *icmphdr = (IcmpHeader*)(buf + iphdrlen);

if (icmphdr->i_type != ICMP_ECHOREPLY)
{
SOCKETDEV_ERROR("Ping [%s], no echo reply", ip, icmphdr->i_type);
return -1;
}

if (icmphdr->i_id != icmp_id)
{
//SOCKET_LOG_ERROR("Ping [%s] error magic packet", ip);
return 1;
}

return 0;
}

static bool send_icmp_data(SOCKET sock_raw, const char * ip, USHORT icmp_id,  int try_count, int timeout)
{
int seq_no = 0;
char icmp_data[MAX_PACKET] = {0};

sockaddr_in dest = {0};
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr( ip );

bool success = false;
bool needToSendPingReq = true;
while(try_count-- > 0)
{
bool isToSendPingReq = needToSendPingReq;
needToSendPingReq = true;
if(isToSendPingReq)
{
memset(icmp_data,0,MAX_PACKET);
fill_icmp_data(icmp_data, DEF_PACKET_SIZE, icmp_id);

((IcmpHeader*)icmp_data)->i_cksum = 0;
((IcmpHeader*)icmp_data)->timestamp = GetTickCount();

((IcmpHeader*)icmp_data)->i_seq = (unsigned short)seq_no++;
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,
DEF_PACKET_SIZE);

int bwrote = sendto(sock_raw, icmp_data, DEF_PACKET_SIZE, 0, (struct sockaddr*)&dest,  sizeof(dest));

if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
SOCKETDEV_ERROR("Ping [%s] timed out", ip);
continue;
}

SOCKETDEV_ERROR("Ping [%s] packet send failed: %d", ip, WSAGetLastError());
continue;
}

if (bwrote < DEF_PACKET_SIZE )
{
continue;
}
}

char recvbuf[MAX_PACKET] = {0};
struct sockaddr_in from;
int fromlen = sizeof(from);

FdSet fdset;
fdset.setRead(sock_raw);
timeval val;
val.tv_sec = timeout / 1000;
val.tv_usec = (timeout%1000)*1000;

int bread = SOCKET_ERROR;
bool bTimeout =false;
do
{
int selectRet = fdset.select(val);
if(selectRet == 0)
{//超时
bTimeout = true;
break;
}
else if(selectRet < 0)
{//发生错误
break;
}
if(fdset.readyToRead(sock_raw))
{
bread = recvfrom(sock_raw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
}
} while (0);

if(bTimeout || (bread <= 0 && WSAGetLastError() == WSAETIMEDOUT))
{
SOCKETDEV_ERROR("Ping [%s] timed out", ip);
continue;
}

if (bread <= 0)
{
SOCKETDEV_ERROR("Ping [%s] recv failed: %d", ip, WSAGetLastError());
continue;
}

int ret = decode_resp(recvbuf, bread, &from, ip, icmp_id);
if(ret == 0)
{
success = true;
break;
}
else if(ret == 1)
{
//接收了别的ping回复,需要重新接收
needToSendPingReq = false;
++ try_count;
continue;
}
//失败
continue;
}

return success;
}

//ping
bool Ping(const char * ip, int try_count, int timeout)
{
SOCKET sock_raw;

bool success = false;

USHORT icmp_id = MakeIcmpID();

do
{
sock_raw = WSASocket (AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);

if (sock_raw == INVALID_SOCKET)
{
SOCKETDEV_ERROR("WSASocket failed");
break;
}

unsigned int beginTick = GetTickCount(); //统计时间开始

char icmp_data[MAX_PACKET] = {0};
fill_icmp_data(icmp_data, MAX_PACKET, icmp_id);
success = send_icmp_data(sock_raw, ip, icmp_id, try_count, timeout);

unsigned int endTick = GetTickCount();  //统计时间结束
if (success)
{
SOCKETDEV_INFO("Ping [%s] success, use %u ms", ip, (endTick - beginTick) );
}
else
{
SOCKETDEV_ERROR("Ping [%s] failed, use %u ms", ip, (endTick - beginTick));
}

} while (0);

if (sock_raw != INVALID_SOCKET)
{
closesocket(sock_raw);
}

return success;
}

/*lint +e830*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: