如何獲取 iOS 的網(wǎng)絡(luò)流量數(shù)據(jù)

最近在做直播相關(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 中,這樣也是不允許的宪睹。

Swift Complier - Search Path - Import Path
Swift Complier - Search Path - Import Path

在你的項(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 那就又是下一話了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涌韩,一起剝皮案震驚了整個濱河市畔柔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌臣樱,老刑警劉巖靶擦,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雇毫,居然都是意外死亡玄捕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門棚放,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枚粘,“玉大人,你說我怎么就攤上這事飘蚯《慕幔” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵孝冒,是天一觀的道長。 經(jīng)常有香客問我拟杉,道長庄涡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任搬设,我火速辦了婚禮穴店,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拿穴。我一直安慰自己泣洞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布默色。 她就那樣靜靜地躺著球凰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腿宰。 梳的紋絲不亂的頭發(fā)上呕诉,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機(jī)與錄音吃度,去河邊找鬼甩挫。 笑死,一個胖子當(dāng)著我的面吹牛椿每,可吹牛的內(nèi)容都是我干的伊者。 我是一名探鬼主播英遭,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼亦渗!你這毒婦竟也來了挖诸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤央碟,失蹤者是張志新(化名)和其女友劉穎税灌,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亿虽,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡菱涤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洛勉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粘秆。...
    茶點(diǎn)故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖收毫,靈堂內(nèi)的尸體忽然破棺而出攻走,到底是詐尸還是另有隱情,我是刑警寧澤此再,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布昔搂,位于F島的核電站,受9級特大地震影響输拇,放射性物質(zhì)發(fā)生泄漏摘符。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一策吠、第九天 我趴在偏房一處隱蔽的房頂上張望逛裤。 院中可真熱鬧,春花似錦猴抹、人聲如沸带族。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝙砌。三九已至,卻和暖如春跋理,著一層夾襖步出監(jiān)牢的瞬間拍霜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工薪介, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祠饺,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓汁政,卻偏偏與公主長得像道偷,于是被迫代替她去往敵國和親缀旁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 核心能力是當(dāng)前國外教育和外語教育界的一個熱點(diǎn)問題懊渡。在2015 年的中心工作會議上我談到,根據(jù)我國的社會文化情境军拟,使...
    靜_靜_閱讀 2,324評論 0 5
  • 文/韋劍 刺破最后一抹黑暗 你把萬物從沉睡中拉醒 在枝繁葉茂間剃执,在綠草上 曬干了昨晚凝聚的晶珠 你卻不知 還有路燈...
    韋劍閱讀 182評論 0 1
  • 前幾天看到“道德綁架”這個說法辫继,上網(wǎng)查了一下怒见,所謂的“道德綁架”,是在用圣人的標(biāo)準(zhǔn)要求普通人姑宽,用美德來要求道德義務(wù)...
    胡義華閱讀 2,244評論 0 1
  • 周日遣耍,與朋友約,暴走西湖一整圈炮车。38830步配阵,24.19公里,早9點(diǎn)到晚8點(diǎn)示血,結(jié)束一天的行程。腳殘救拉,已累癱难审。 晚上...
    Cherish5240閱讀 349評論 0 2