#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*/