Swift - PingManager,同時ping多個域名

??因為項目需求,需要同時對幾十個服務器進行短時間(2秒內)的高頻(每0.2秒發一次)ping,獲取各個服務器的平均速度。在網上找了很久,都沒有合適的庫。
??開始的時候用官方的SimplePing,但是這個庫會阻塞主線程,導致app出現卡頓。
??然后找到了一個基于OC的庫GBPing,這個庫試用了一下,它并不會阻塞主線程,而是獨立創建兩條線程用于收發包,結果處理后返回給主線程再做相應的操作。但是,在我同時ping幾十條服務器的時候,它就有可能出現閃退,原因是短時間內創建調用的線程資源太多,最后出現閃退。
??為了解決以上問題,并且將其轉換成swift語言,根據GBPing的收發包原理,決定自定義一個PingMananger類并重寫GBPing,這個類主要用于管理多個ping實例,把它們的初始化和收發包都控制在固定的線程中,最大化減少線程資源的浪費,這個類有三個自定義的隊列,一個用于初始化,開始和停止ping并處理ping結果,一個用于發包,一個用于收包。
效果圖:

單個域名.gif
多個域名.gif

??以下介紹使用步驟,順便介紹一下原理。
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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容