iOS 長連接-GCDAsyncSocket 使用

本篇文章僅介紹本人在公司項目中使用GCDAsyncSocket建立socket連接中使用的一些方法和心得體會惨篱。對GCDAsyncSocket方法不熟悉的同學可以先查看GCDAsyncSocket API簡介這篇文章荐操。

初始化

initWithDelegate:delegateQueue:

初始化,設置委托和委托隊列毒返。
內部會在初始化時保存?zhèn)魅氲奈袑ο蠹拔袑玖纯欤瑒?chuàng)建套接字隊列险领,初始化socket4FD(本地IPV4Socket)凡辱、socket6FD(本地IPV6Socket)戒职、socketUN(unix域的套接字)、socketUrl(unix域 服務端 url)透乾、stateIndex(狀態(tài)Index)洪燥、readQueue(讀隊列)、currentRead(當前讀入數(shù)據(jù)包)乳乌、writeQueue(寫隊列)蚓曼、currentWrite(當前寫入數(shù)據(jù)包)、preBuffer(公用緩沖區(qū))钦扭、alternateAddressDelay(連接備選服務端地址的延時 ,另一個IPV4或IPV6床绪,默認0.3S)等參數(shù)客情。

初始化完成,開啟連接(如果是服務端癞己,就需要去bind端口膀斋,并且accept,等待客戶端的連接痹雅。)

連接

connectToHost:onPort:withTimeout:error:

1.host及端口校驗
2.代理隊列校驗仰担、是否開始連接(kSocketStarted)、是否支持IPV4 IPV6绩社、清空讀寫隊列
3.標記Socket為開始連接(kSocketStarted)進行一下異步連接并開啟連接超時設置
4.根據(jù)host port摔蓝,去獲取server地址信息(異步,DNS解析,NSData類型)愉耙。
5.創(chuàng)建server地址連接(耗時贮尉,異步,完成后回調)
根據(jù)第4步中地址創(chuàng)建socket(返回socket的文件描述符朴沿,int類型猜谚,scoket其實就是Int類型)
->Socket綁定本機地址(本項目無特定端口败砂,此步驟在連接服務器步驟connect自動完成端口綁定)
->連接服務器

成功:關閉無用socket->添加‘已連接’連接狀態(tài)(kConnected)->關閉連接超時設置->創(chuàng)建讀寫流及讀寫回調注冊
->回調成功代理(socket:didConnectToHost:port:返回數(shù)據(jù)為根據(jù)socket的文件描述符獲取的服務器IP及端口)
->本機socket設置相關參數(shù)->開啟讀寫

失敗:關閉當前socket并置空->清空讀寫隊列->退出讀寫監(jiān)聽->標記Socket連接狀態(tài)為0->回調斷開代理(socketDidDisconnect:withError:魏铅,error不為空昌犹,且socket已開始連接)

PS:當然連接過程不僅上述過程(如:連接超時設置等)且具體步驟未詳細描述,本處列出的為本項目連接過程或需引起注意過程览芳。

注意:連接之前判斷當前socket連接狀態(tài)斜姥,避免重復連接。

  • 個人疑點

斷開就清空讀寫隊列路操?疾渴??

斷開

disconnect
立即斷開連接(同步)屯仗。所有掛起的讀取或寫入操作都將被丟棄搞坝。

_localAsyncSocket.delegate = nil;
[_localAsyncSocket disconnect];
_localAsyncSocket = nil;

readDataWithTimeout:tag:

  • 個人思考

由于項目中長連接模塊有心跳業(yè)務且心跳有返回值,所以通過設置超時時間來間接判斷長連接通道通暢性魁袜。

writeData: withTimeout: tag:

  • 個人思考

GCDAsyncSocket的更強大功能之一是其排隊的體系結構桩撮。這使您可以在方便時控制套接字,而不是在套接字告知您已準備就緒時對其進行控制峰弹。 ——引用至Reference_GCDAsyncSocket

雖然GCDAsyncSocket認為其讀寫排隊的體系結構是一項很強大的功能之一店量,然而本人卻并不這樣認為,而且直接使用其讀寫隊列還可能引起一系列的問題鞠呈。例如IM類項目融师,必須要保證每條消息的成功發(fā)送,此時如果我們直接使用writeData: withTimeout: tag:方法蚁吝,如果當前網(wǎng)絡不佳導致socket斷開(socketDidDisconnect:withError:)旱爆,此時會清空未處理的讀寫隊列,這樣必然會導致消息的丟失窘茁。

個人總感覺GCDAsyncSocket開發(fā)者在架構中未考慮重連的情況怀伦。

連接判斷

isDisconnected

GCDAsyncSocket內部判斷是否標記連接狀態(tài)為kSocketStarted:已標記,返回NO山林,表示未斷開房待;未標記,返回YES驼抹,表示已斷開桑孩。
項目中在開啟連接(connectToHost:onPort:withTimeout:error:)之前調用此方法判斷是否已開啟連接,做容錯處理框冀。
不推薦外部使用洼怔。

isConnected

GCDAsyncSocket內部判斷是否標記連接狀態(tài)為kConnected:已標記,返回YES左驾,表示已成功連接镣隶;未標記极谊,返回NO,表示未成功連接安岂。
由于項目要求實時性比較高轻猖,所以在發(fā)送之前會使用本方法做通道通暢性的判斷:YES,發(fā)送域那;NO咙边,不發(fā)送,根據(jù)業(yè)務做相應處理次员。
推薦外部使用败许,如項目中連接狀態(tài)便是使用此方法判斷。

tag

  • 個人思考

通過上面讀寫方法的介紹可知在每個讀寫方法中都會傳入一個tag值淑蔚,傳遞的tag值會在代理中回傳給使用者市殷。因此可以在寫入(writeData: withTimeout: tag:)時為消息標記不同的tag值,然后通過代理方法(socket: didWriteDataWithTag:)中返回的tag值來判斷消息完成寫入刹衫。

有時我們會寫入請求類消息醋寝,服務器收到請求后會返回某些信息,雖然我們可以在寫入完成的代理方法(socket: didWriteDataWithTag:)中設置讀消息(readDataWithTimeout:tag)相同的tag值带迟,但是依然不能通過tag值來判斷讀入的消息為寫入消息的返回數(shù)據(jù)音羞,因為此時服務器很可能會主動推一條與本次寫入的請求類消息毫無關聯(lián)的信息。

重連

我們在代理回調的斷開(socketDidDisconnect:withError:)方法中判斷本次斷開返回的error值是否為空仓犬,如果不為空則表示本次斷開為異常斷開嗅绰,開啟一次延時重連。注意在主動斷開連接的方法中要取消本次延時重連搀继。

ping

我們在異常斷開時會調用RealReachability進行ping操作办陷,主要用于判斷異常斷開時是否由網(wǎng)路異常引起,以及網(wǎng)路可用時是否能夠正常重連律歼。

  • 具體實現(xiàn)
    設置hostForPing值為長連接地址,hostForCheck為"www.apple.com"啡专,同時根據(jù)需求對RealReachability庫進行了部分修改险毁,使其在ping結果的block中返回該次ping是否成功,是否使用VPN,網(wǎng)絡狀況们童,ping地址畔况。返回結果如下:

    1.是否有可用網(wǎng)絡
    否:返回ping失敗,未使用VPN慧库,網(wǎng)絡狀態(tài)跷跪,hostForPing
    2.是否使用VPN
    是:返回ping失敗,使用VPN齐板,網(wǎng)絡狀態(tài)吵瞻,hostForPing
    3.ping hostForPing
    成功:返回ping成功葛菇,未使用VPN,網(wǎng)絡狀態(tài)橡羞,hostForPing
    失斆型!:進行4步驟
    4.是否使用VPN
    是:返回ping失敗,使用VPN卿泽,網(wǎng)絡狀態(tài)莺债,hostForCheck
    5.ping hostForCheck(延時1S)
    返回ping結果,未使用VPN签夭,網(wǎng)絡狀態(tài)齐邦,hostForCheck

通過對比對應的ping結果及當時的網(wǎng)絡狀況對比可以得出該次斷開是否有網(wǎng)絡引起。

同時記錄本次為第幾次連接(從發(fā)起到連接成功算一次第租,在連接成功的方法中進行+1操作)及本次連接失敗后重連次數(shù)(需在連接成功的方法中將該參數(shù)置0)措拇,通過兩個參數(shù)值與網(wǎng)絡狀態(tài)對比,可以判斷網(wǎng)路可用時是否能夠正常重連煌妈。

通過日志分析得出:

1.長連接斷開后儡羔,ping失敗,無可用網(wǎng)絡璧诵,占比80%左右汰蜘。
2.長連接斷開后,ping成功(后續(xù)重連成功)之宿,占比20%左右族操。
3.長連接斷開后,ping成功比被,但是持續(xù)異常斷開色难,偶現(xiàn)。
異常斷開code:60等缀,61枷莉。均為電信網(wǎng)絡,安卓用戶也存在類似情況尺迂,懷疑網(wǎng)絡服務引起笤妙。


參考資料

感謝涂耀輝Cooci_和諧學習_不急不躁兩位大神噪裕,兩位大神在其博客上有關于GCDAsyncSocket連接蹲盘、讀寫、斷開膳音、粘包等功能詳細的文檔(下面的鏈接)介紹召衔。
強烈建議各位同學在著手開發(fā)之前先認真閱讀兩位大神的文檔,對GCDAsyncSocket有整體的了解祭陷,這樣可以充分利用GCDAsyncSocket已有的功能苍凛,避免開發(fā)過程中遇到疑難的問題趣席,同時也方便后續(xù)bug的修改。而本人也是這樣做的毫深。

iOS即時通訊進階 - CocoaAsyncSocket源碼解析(Connect篇)
iOS即時通訊進階 - CocoaAsyncSocket源碼解析(Connect篇終)
iOS即時通訊下數(shù)據(jù)粘包吩坝、斷包處理實例(基于CocoaAsyncSocket)
iOS即時通訊進階 - CocoaAsyncSocket源碼解析(Read篇)
iOS即時通訊進階 - CocoaAsyncSocket源碼解析(Read篇終)
CocoaAsyncSocket源碼分析---Write
CocoaAsyncSocket源碼解析---終
CocoaAsyncSocket源碼注釋(2017)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者哑蔫。
  • 序言:七十年代末钉寝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闸迷,更是在濱河造成了極大的恐慌嵌纲,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腥沽,死亡現(xiàn)場離奇詭異逮走,居然都是意外死亡,警方通過查閱死者的電腦和手機今阳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門师溅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人盾舌,你說我怎么就攤上這事墓臭。” “怎么了妖谴?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵窿锉,是天一觀的道長。 經(jīng)常有香客問我膝舅,道長嗡载,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任仍稀,我火速辦了婚禮洼滚,結果婚禮上,老公的妹妹穿的比我還像新娘技潘。我一直安慰自己遥巴,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布崭篡。 她就那樣靜靜地躺著,像睡著了一般吧秕。 火紅的嫁衣襯著肌膚如雪琉闪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天砸彬,我揣著相機與錄音颠毙,去河邊找鬼斯入。 笑死,一個胖子當著我的面吹牛蛀蜜,可吹牛的內容都是我干的刻两。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼滴某,長吁一口氣:“原來是場噩夢啊……” “哼磅摹!你這毒婦竟也來了?” 一聲冷哼從身側響起霎奢,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤户誓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后幕侠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帝美,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年晤硕,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼潭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡舞箍,死狀恐怖舰褪,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情创译,我是刑警寧澤抵知,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站软族,受9級特大地震影響刷喜,放射性物質發(fā)生泄漏。R本人自食惡果不足惜立砸,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一掖疮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颗祝,春花似錦浊闪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倔幼,卻和暖如春盖腿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工翩腐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸟款,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓茂卦,卻偏偏與公主長得像何什,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子等龙,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355