??因為項目需求,需要同時對幾十個服務器進行短時間(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