因为项目需求,需要同时对几十个服务器进行短时间(2秒内)的高频(每0.2秒发一次)ping,获取各个服务器的平均速度。在网上找了很久,都没有合适的库。
开始的时候用官方的SimplePing,但是这个库会阻塞主线程,导致app出现卡顿。
然后找到了一个基于OC的库GBPing,这个库试用了一下,它并不会阻塞主线程,而是独立创建两条线程用于收发包,结果处理后返回给主线程再做相应的操作。但是,在我同时ping几十条服务器的时候,它就有可能出现闪退,原因是短时间内创建调用的线程资源太多,最后出现闪退。
为了解决以上问题,并且将其转换成swift语言,根据GBPing的收发包原理,决定自定义一个PingMananger类并重写GBPing,这个类主要用于管理多个ping实例,把它们的初始化和收发包都控制在固定的线程中,最大化减少线程资源的浪费,这个类有三个自定义的队列,一个用于初始化,开始和停止ping并处理ping结果,一个用于发包,一个用于收包。
效果图:
以下介绍使用步骤,顺便介绍一下原理。
1、首先创建一个ping实例,并设置代理和目标地址,然后附加到PingManager的pings数组上
let ping = Ping()
ping.delegate = self
ping.host = ip
PingMannager.shared.add(ping)
设置代理后需要实现相应的代理方法。
2、调用PingManager的setup
PingMannager.shared.setup {
}
查看setup方法的实现:
func setup(_ callBack:(()->())? = nil){
var newPings = self.pings
let pings = self.pings
weak var weakSelf = self
mainQueue.async {
for ping in pings{
weak var weakPing = ping
let setupBlock = {()->() in
weakPing?.setup { (success, error) in
if success{
weakPing?.startPinging()
}else{
newPings.removeAll(where: { (delete) -> Bool in
return delete.host == weakPing?.host
})
}
}
}
setupBlock()
}
weakSelf?.isSettingUp = false
weakSelf?.pings = newPings
callBack?()
}
}
在PingManager的主线程队列中处理Ping实例的初始化。
3、在回调中设置timeout(超时时间)和period(ping间隔),以及开始ping
PingMannager.shared.setup {
PingMannager.shared.timeout = 1
PingMannager.shared.pingPeriod = 1
PingMannager.shared.startPing()
}
startPing的实现如下:
func startPing(){
if !self.isPinging{
self.isPinging = true
send()
listen()
}
}
这个不用解释太多,重点是send和listen两个方法:
private func send(){
weak var weakSelf = self
mainQueue.async {
if weakSelf?.isPinging == true{
weakSelf?.pings.removeAll(where: { (ping) -> Bool in
return ping.isPinging == false
})
if weakSelf?.pings.count ?? 0 > 0 {
let pings = self.pings
weakSelf?.sendQueue.async {
autoreleasepool{
let runUntil = CFAbsoluteTimeGetCurrent() + (weakSelf?.pingPeriod ?? 1)
for ping in pings{
ping.send()
}
var time : TimeInterval = 0;
while (runUntil > time) {
let runUntilDate = Date(timeIntervalSinceReferenceDate: runUntil)
RunLoop.current.run(until: runUntilDate)
time = CFAbsoluteTimeGetCurrent()
}
weakSelf?.send()
}
}
}
}
}
}
private func listen(){
weak var weakSelf = self
mainQueue.async {
if weakSelf?.isPinging == true{
weakSelf?.pings.removeAll(where: { (ping) -> Bool in
return ping.isPinging == false
})
if weakSelf?.pings.count ?? 0 > 0 {
let pings = self.pings
weakSelf?.listenQueue.async {
autoreleasepool{
for ping in pings{
ping.listenOnce()
}
weakSelf?.listen()
}
}
}
}
}
}
首先在主队列中处理send和listen队列都用到的属性,然后在send队列中开始发送icmp包,发送完后等待下次发送时间,然后再次发送。linsten方法也是差不多,唯一不同的是不需要等待。
总结一下使用步骤:
for host in hostArray{
let ping = Ping()
ping.delegate = self
ping.host = host
PingMannager.shared.add(ping)
}
PingMannager.shared.setup {
PingMannager.shared.timeout = self.timeout
PingMannager.shared.pingPeriod = self.period
PingMannager.shared.startPing()
}
很简单吧,有需要就下载使用吧
PingManager
如有问题,请联系289193866@qq.com