手機網絡連接狀態的檢測對於 iOS App 開發來說是一個非常基礎的需求,在前一篇文章 蘋果示例源碼閱讀:Reachability 我們介紹了如何通過 SCNetworkReachability
提供的一系列 C 函數 API 進行網絡連接狀態變化的監聽。但事實上,此方案能獲取的只是設備的本地連接狀態,有時它很難為我們檢測真正的網絡連接狀態,如以下場景:
現在很多的公共場所的 WiFi,需要網頁登錄授權,授權之前無法上網,但本地連接已經建立;
存在了本地網絡連接,但信號很差,實際無法連接到服務器;
iOS 連接的路由設備本身沒有連接外網等。
ping
是 Windows、Unix 、Linux 和 macOS 等系統下一個常用的命令,利用 ping
命令可以用來測試數據包 (ICMP) 能否通過 IP 協議到達特定主機,並收到主機的應答,以檢查網絡是否連通和網絡連接速度,幫助我們分析和判定網絡故障。
幸運的是,蘋果為我們提供了示例源碼:SimplePing,示范了在 iOS 或者 Mac 上如何用 Objective-C / Swift 實現 ping
操作,因此我們也可以通過 ping
來檢查手機網絡的真實連接狀態。事實上,Github 上著名的第三方開源庫 RealReachability 也是這麼做的。
對於 SimplePing
源碼的閱讀,我們將分為兩部分來介紹。第一部分將結合 SimplePing.h
頭文件裡聲明的方法,介紹如何使用 SimplePing
類封裝的方法進行 ping
操作,第二部分(下一篇)將詳細介紹 SimplePing.m
裡各方法的具體實現細節。
通過 SimplePing.h
頭文件中的聲明,我們整理 SimplePing
的類結構如下圖所示:
?
下面我們一一介紹 SimplePing
類的各個屬性、方法以及 delegate 回調方法的含義及作用。
SimplePing
中,禁用了 init
方法,只提供 initWithHostName:
一個方法,它可以初始化一個用於 ping 指定的主機實例對象。其中 hostName
參數可以是主機的 DNS 域名,或者是 IPv4、IPv6 地址的字符串形式。
hostName
:只讀,保存由初始化方法 initWithHostName:
傳入的 ping
操作要連接的主機域名或 IP 地址。
addressStyle
:主機的 IP 地址類型,如 IPv4
或 IPv6
等,其中 SimplePingAddressStyle
枚舉類型的定義如下:
hostAddress
:只讀,在 start
方法調用之後,根據 hostName
得到的要 ping 的主機的 IP 地址,它是 struct sockaddr
形式的 NSData 數據。當 SimplePing 實例處於 stopped
狀態,或者實例調用了 start
方法,但在 simplePing:didStartWithAddress:
方法被調用之前,hostAddress
的值都是 nil
。
hostAddressFamily
:只讀,hostAddress
的地址族,如果 hostAddress
為 nil,則其值為:AF_UNSPEC
。
identifier
:只讀,當創建一個 SimplePing
實例對象時,會自動生成一個的隨機的標識符,用來唯一標識當前 ping 對象。
nextSequenceNumber
:只讀,ping 每發送一次數據包都會有一個對應的序列號(sequence number
),此值為下一次 ping 操作要發送數據時的序列號,從 0 開始遞增,當 ping 成功發送一次數據到主機並收到應答時,該值 +1。而對於本次 ping 的 sequence number
在成功發送數據(request)和成功接收到響應(response)的 delegate 回調方法裡都會以方法參數返回,以便進行 ping 操作耗時的計算等等。
delegate
:當前對象的回調,delegate 中的回調方法將在對象調用 start
方法所在的線程對應的 run loop
中以默認的 run loop model
執行。
start 方法
:開始一個 ping
操作,在調用此方法前,必須給 SimplePing
實例對象的 delegete
以及其他參數賦值。當 start
方法成功執行時,會回調 delegate 中的 simplePing:didStartWithAddress:
方法,在該回調方法裡,就可以通過 sendPingWithData:
開始發送 ICMP
數據包,並等待接受主機應答的數據包。另外需要注意的是,當一個實例已經 started
,又一次調用此 start
方法會出錯。
sendPingWithData: 方法
:向主機發送特定格式的 ICMP 數據包,調用此方法前必須保證實例已經 started
並且要等待 simplePing:didStartWithAddress:
回調執行才能開始發送數據。參數 data
為要向主機發送的 ICMP 數據包,可以為 nil
,默認會發一個標准的 64 byte 數據包。
stop 方法
:當結束要 ping
操作時,調用此方法。與 start
方法不同的是,當一個實例已經 stopped
,再次調用此方法也沒事。
start
方法執行結果的回調:
sendPingWithData:
方法執行結果的回調,每發送一次數據,都會同步地回調以下兩個方法其中一個(除非你在發送途中調用了 stop
方法):
接收到主機返回應答數據的回調:
注:以上回調方法中的 packet
數據包只包含了 ICMP
header 和 sendPingWithData:
中傳入的數據,但不包含任何 IP 層的 header。
根據蘋果提供的 Demo,我們梳理了一下使用 SimplePing
類進行 ping
操作的流程如下圖所示:
?
根據上圖,我們寫了一個簡單的使用示例,詳見下面代碼以及注釋,不再贅述。
本篇文章只介紹了如何使用蘋果提供的示例源碼 SimplePing
類初始化一個實例在 iOS 設備上進行 ping 操作,以進行判斷網絡真實連接狀態,在下一篇文章《蘋果實例源碼閱讀:SimplePing(2)》,我們將介紹 SimplePing
類的各個方法的具體內部實現。
Apple Sample Code: SimplePing
iOS下的實際網絡連接狀態檢測:RealReachability