异步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报文头结构:
然后是实现代码:
这样一个发送请求和等待回应的过程,根据网络情况都会有一定的延时,那如何能提高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缓存开大,以防止缓存溢出导致的丢包:
通过抓包,还可以看到,如果本地主机的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));
相关文章推荐
- 如何在Linux下实现设备的配置(下)
- 实例讲解Android中如何实现图片的异步加载功能
- 如何实现图片的异步加载
- 如何在MAC机器中实现移动设备WiFI上网(没有专门的无线路由器的情况)
- 教你如何封装异步网络连接NSURLConnection实现带有百分比的下载
- 如何用TensorFlow在安卓设备上实现深度学习推断
- 大数据IMF 传奇 8台设备如何实现免密码的SSH登录呢 ?脚本分发 解决方案
- PHP如何实现异步数据调用
- 设备中如何实现类似gopro给iphone分配ip但是不修改dns的功能
- 前端如何实现异步加载
- python_如何通过twisted实现数据库异步插入?
- 如何使用CNN推理机在IoT设备上实现深度学习
- 如何实现同步等待异步事件发生
- 如何实现图片的异步加载
- 方法:如何解决用MFC实现的ping功能中把目标主机不可到达的当成ping通的问题
- 如何让file input能够实现置空和【异步】上传后相同文件的再次选择触发change
- linux设备驱动第三篇:如何实现一个简单的字符设备驱动
- 实例讲解Android中如何实现图片的异步加载功能
- WordPress中如何实现Ping功能
- 如何jax异步实现附件上传工作