最近在做直播相關(guān)的一些內(nèi)容唆阿,其中一個需求就是在直播時暖夭,能夠?qū)崟r地顯示當(dāng)前的網(wǎng)絡(luò)狀況睦授,包括上下行的速度與累計(jì)使用的流量两芳。遂做了一些相關(guān)的研究,發(fā)現(xiàn)所有的檢索結(jié)果都指向了 ifaddrs.h
去枷。依照文件頭部的版權(quán)申明怖辆, ifaddrs.h
來自 FreeBSD 的項(xiàng)目是复,版權(quán)屬于 Berkeley Software Design, Inc.。
FreeBSD 是一種自由的類 Unix 操作系統(tǒng)竖螃,它起源于 AT&T Unix淑廊,是經(jīng)過 BSD 、 386BSD 和 4.4BSD 發(fā)展而來的類 Unix 的一個重要分支特咆。
—— 摘自維基百科
當(dāng)然追根溯源并不是今天的重點(diǎn)季惩,而且網(wǎng)上一堆 OC 的現(xiàn)成例子,抄了就能用腻格。不過作為一名 Swift 老司機(jī)蜀备,怎么用 Swift 做實(shí)現(xiàn)才是一名好司機(jī)的關(guān)鍵。下面我們發(fā)車荒叶!
如何自己將 C.h 封裝成一個 Module
首先,基于扁平與模塊化的思想输虱,直接將 ifaddrs.h
放到 Objective-C bridging header
做橋接肯定是不妥的些楣,而且如果要將其加到一個 framework 中,這樣也是不允許的宪睹。
在你的項(xiàng)目中愁茁,定位到你 PROJECT 的 Build Setting,過濾器中可以輸入一個 import
亭病,然后找到 Swift Complier - Search Path
大項(xiàng)中的 Import Path
鹅很。這里你可以按照平臺劃分,來加入一些 modulemap 的檢索路徑罪帖。你實(shí)際輸入的時候可能是這樣的 $(SRCROOT)/SystemModule/ifaddrs/iphoneos
促煮。
而 module.modulemap 文件的內(nèi)容,此處以 iphoneos 平臺為例整袁,至少需要同時支持 iphoneos 與 iphonesimulator菠齿,不同平臺的具體路徑可以依葫蘆畫瓢,檢索一下即可:
module ifaddrs {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/ifaddrs.h"
export *
}
然后坐昙,你就可以在你的項(xiàng)目中直接 import ifaddrs
了绳匀。
getifaddrs(_:) -> Int32
函數(shù)的一些使用說明與 Swift 指針的初見
該函數(shù)可以獲取所有系統(tǒng)的網(wǎng)絡(luò)接口的信息,不僅僅是全局的聯(lián)網(wǎng)數(shù)據(jù)炸客,同時 IP 地址也可以從這里獲取疾棵。
此時你已經(jīng)可以看到 getifaddrs(_:) -> Int32
在 Swift 下面的具體方法簽名了
public func getifaddrs(_: UnsafeMutablePointer<UnsafeMutablePointer<ifaddrs>?>!) -> Int32
少年,回想起當(dāng)年第一次面對 C 語言的指針痹仙、指針的指針的恐懼了嗎是尔?UnsafeMutablePointer
即為 Swift 下可變指針的具體類型,至少比星號看起來舒服多了蝶溶。此處需要構(gòu)造一個指針的指針嗜历,實(shí)際類型為 ifaddrs
宣渗。
var addrsPointer: UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&addrsPointer) == 0 {
// Do something
}
getifaddrs(_:) -> Int32
函數(shù)會創(chuàng)建一個鏈表,鏈表上的每個節(jié)點(diǎn)都是一個 ifaddrs
結(jié)構(gòu)體梨州,并返回鏈表第一個元素的指針痕囱。成功返回 0 , 失敗返回 -1 。并在最后使用 freeifaddrs(_:)
函數(shù)來釋放申請的內(nèi)存空間暴匠。
var pointer = addrsPointer
while pointer != nil {
// Do something
pointer = pointer?.pointee.ifa_next
}
freeifaddrs(addrsPointer)
注意:在 Swift3 中鞍恢,指針取其實(shí)際的對象的方法已從 memory
變成了 pointee
,其具體的簽名為:
public var pointee: Pointee { get nonmutating set }
通過判斷每一個 ifaddrs
結(jié)構(gòu)體的 ifa_addr
屬性的 sa_family
字段是否為 AF_LINK
來過濾進(jìn)行流量監(jiān)控內(nèi)容
if let addrs = pointer?.pointee {
let name = String(cString: addrs.ifa_name)
if addrs.ifa_addr.pointee.sa_family == UInt8(AF_LINK) {
// Do something
}
}
最后根據(jù)其 name 來判斷流量屬于 Wi-Fi 還是 WWAN每窖。
這里還有一個小坑帮掉,ifaddrs
結(jié)構(gòu)體的 ifa_data
字段的類型是 UnsafeMutableRawPointer!
。而目標(biāo)需要使用的類型窒典,或者說它實(shí)際的類型是 if_data
蟆炊。如果你直接使用 if let
編譯器會告訴你這是不相關(guān)的類型,無法成功轉(zhuǎn)換瀑志。此處需要使用 Swift 標(biāo)準(zhǔn)庫中的 unsafeBitCast
的方法涩搓,其具體簽名為:
public func unsafeBitCast<T, U>(_ x: T, to: U.Type) -> U
引用 王巍 @onevcat 的原話:
unsafeBitCast
是非常危險(xiǎn)的操作,它會將一個指針指向的內(nèi)存強(qiáng)制按位轉(zhuǎn)換為目標(biāo)的類型劈猪。因?yàn)檫@種轉(zhuǎn)換是在 Swift 的類型管理之外進(jìn)行的昧甘,因此編譯器無法確保得到的類型是否確實(shí)正確,你必須明確地知道你在做什么战得。
—— 原文《Swift 中的指針使用》
完整代碼如下:
if getifaddrs(&addrsPointer) == 0 {
var pointer = addrsPointer
while pointer != nil {
if let addrs = pointer?.pointee {
let name = String(cString: addrs.ifa_name)
if addrs.ifa_addr.pointee.sa_family == UInt8(AF_LINK) {
if name.hasPrefix("en") { // Wifi
let networkData = unsafeBitCast(addrs.ifa_data, to: UnsafeMutablePointer<if_data>.self)
result.wifi.received += networkData.pointee.ifi_ibytes
result.wifi.sent += networkData.pointee.ifi_obytes
} else if name.hasPrefix("pdp_ip") { // WWAN
let networkData = unsafeBitCast(addrs.ifa_data, to: UnsafeMutablePointer<if_data>.self)
result.wwan.received += networkData.pointee.ifi_ibytes
result.wwan.sent += networkData.pointee.ifi_obytes
}
}
}
pointer = pointer?.pointee.ifa_next
}
freeifaddrs(addrsPointer)
}
結(jié)語
至此充边,你已經(jīng)拿到全局級別的網(wǎng)絡(luò)數(shù)據(jù),注意單位是 bytes
常侦,至于怎么轉(zhuǎn)化為最終使用的 1.024 kb/s
或是 已使用 10.24 MB
浇冰,我相信已經(jīng)難不倒各位老司機(jī)了。當(dāng)然刮吧,如果你是一名只專注上車的乘客湖饱,不如試一下我已經(jīng)做好的封裝 TrafficPolice 。其實(shí)杀捻,當(dāng)我第一次看到 Traffic 一詞有流量的意思井厌,我也是表示,英語已經(jīng)全還給老師了致讥,囧仅仆。當(dāng)然,上車注意請刷卡(加星9父ぁ)墓拜。暫時僅支持 Carthage 部署,不是我懶请契,的確是想為這么好的工具打一次硬廣咳榜,您就試一下唄夏醉。至于如何支持 CocoaPod 那就又是下一話了。