您的位置:首页 > 其它

异步ping的实现,如何在10秒内ping完20000个设备

2014-04-27 23:50 239 查看
ping报文的原理就是先向特定的ip地址发送一个ping请求消息即ping echo,然后如果对应ip地址的服务器收到这个请求的话就会发送ping回应消息即ping reply。

通过抓包,还可以看到,如果本地主机的arp表里没有对应目的地址的表项,底下网络层会发送arp报文查询目的主机的mac地址,等收到对端的arp响应后,再发送ping echo请求,否则也会ping失败。

正常的一个ping过程代码如下,首先是定义一个icmp报文头结构:

//icmp报文头定义
typedef struct {

unsigned char i_type; //类型

unsigned char i_code; //代码

unsigned short  i_cksum; //校验和

unsigned short  i_id; //标识符

unsigned short  i_seq; //序列号

unsigned long timestamp; //时间戳

} IcmpHead;


然后是实现代码:

bool ping (unsigned long dstIp)
{
//创建raw socket,使用icmp协议
int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

//存放ping请求数据的缓存
char icmp_data[1024];
memset(icmp_data, 0, sizeof(IcmpHead));

//构造请求ping数据
IcmpHead *pHead = (IcmpHead*)icmp_data;

pHead->i_type = ICMP_ECHO;
pHead->i_code = 0;
pHead->i_id=getpid();
pHead->i_seq = 1;
pHead->timestamp = time(0);
pHead->i_cksum = checksum(icmp_data, sizeof(IcmpHead));

//构造目的地址结构
struct sockaddr_in dstAddr = {0};
dstAddr.sin_add.s_addr = dstIp;
dstAddr.sin_family = AF_INET;

//发送ping请求报文
sendto(sockRaw, icmp_data, sizeof(IcmpHead), 0, (struct sockaddr*)&dstAddr, sizeof(sockaddr_in));

//接受ping回应数据
char icmp_reply_data[1024];
struct sockaddr_in srcAddr = {0};
int iRead = recvfrom(sockRaw, icmp_reply_data, 1024,0,&srcAddr, sizeof(sockaddr_in));
if (iRead <=0)
{
return false;
}

//解析回应数据
IcmpHead *pReply = (IcmpHead*)icmp_reply_data;

if ((pReply->i_type == ICMP_ECHOREPLY) &&
(pReply->i_id == getpid() &&
(pReply->i_seq == pHead->i_seq))

{
return true;
}

return false;

}


这样一个发送请求和等待回应的过程,根据网络情况都会有一定的延时,那如何能提高ping的效率以达到对20000个设备每10秒钟就ping一次要求呢。

观察上面的代码,可以知道,等待的时间都阻塞在recvfrom系统调用上,如果对端响应慢了或者完全不响应ping请求,我们还需要给其设定一个超时时间参数,

具体的做法就是在recvfrom调用前,先通过select系统调用并设定好超时时间,来获取对应socket上的读事件,事件到达后再执行recvfrom,否则如果超时时间到了就返回ping失败。

如果要求一次ping大量设备,一个个串行ping过来肯定是不行的,那就并行进行就可以了,并行的方法很多,常用的是多线程方式,即对每个设备单独起线程来ping。

但是设备多的话,对应的线程开销也不可忽略,其实对于ping这种操作来说,完全可以一个线程来完成一组设备的并行ping,采用类似cpu流水线的方法,将一个ping过程拆分成如下几个步骤:

创建socket   ->    构造ping请求   ->     发送ping请求   ->  接收ping响应   ->  解析ping响应

我们可以分析下多个ping任务的话,如何让上面几个步骤如何并行

首先是创建socket,这个是可以所有ping任务重用的,即可以用同一个socket来完成全部的ping报文发送和接收操作。

构造ping请求完全是内存中操作,串行进行时间可以忽略

发送ping请求由于icmp基于ip协议,不需要面向连接,所以也不会阻塞

接收ping响应必须等待对端返回ping回应报文,这个操作最费时,因此应该并行完成

解析ping响应也是内存操作,很快的

通过上面分析,我们的ping操作就需要将等待ping回应这个过程给并行起来,而并行的办法就是一次就发送一组ping请求,然后进入等待所有目的服务器的ping回应。

即过程改造如下:

创建socket   ->    构造ping请求1   ->     发送ping请求1   ->  接收ping响应   ->  解析ping响应

                               构造ping请求2   ->     发送ping请求2                   ^                            │                                   

                                .............                                                                      └──────────┘

                               构造ping请求n   ->     发送ping请求n

在第二步和第三步完成了对所有ping请求的发送,由于是不面向连接的icmp,所以这里不会阻塞,

第四步进入等待任意的ping响应报文到达并立即接收处理,完成后返回继续等待剩下的ping回应,这样整组设备ping的io等待时间就完全并行起来了。

实现时需要注意的是,在一次ping的一组设备数目过大时,比如我们说的20000个设备,可能会出现ping响应风暴,解决的办法是把socket的io缓存开大,以防止缓存溢出导致的丢包:

int hold = 128*1024;

setsockopt(sockRaw, SOL_SOCKET, SO_RECVBUF, &hold, sizeof(int));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: