iOS | Socket & CocoaAsyncSocket 讀/寫(xiě)操作以及粘包處理

本文主要介紹 CocoaAsyncSocket的 讀/寫(xiě) 操作启绰,以及如何處理TCP粘包的問(wèn)題,CocoaAsyncSocket基本使用的請(qǐng)查看上一篇文章Socket & CocoaAsyncSocket介紹與使用

排隊(duì) 讀/寫(xiě) 操作

CocoaAsyncSocket庫(kù)的最佳功能之一是“排隊(duì) 讀/寫(xiě) 操作”狰晚。

  • 寫(xiě)操作: 套接字未連接,但我還是可以開(kāi)始寫(xiě)它,庫(kù)將排隊(duì)我的所有寫(xiě)操作铅碍,在套接字連接后曙蒸,它將自動(dòng)開(kāi)始執(zhí)行我的寫(xiě)操作捌治!
NSError * err = nil ;
NSError *err = nil;
if (![self.clientSocket connectToHost:@"ip地址" onPort: "端口號(hào)" error:&err])  //異步!
{
    NSLog(@"I goofed: %@", err);
}

//此時(shí)套接字未連接纽窟。
//但我還是可以開(kāi)始寫(xiě)它肖油!
//庫(kù)將排隊(duì)我的所有寫(xiě)操作,
//在套接字連接后臂港,它將自動(dòng)開(kāi)始執(zhí)行我的寫(xiě)操作森枪!
   [socket writeData: data1 withTimeout: - 1  tag: 1 ];
   [socket writeData: data2 withTimeout: - 1  tag: 2 ];

  • 排隊(duì)讀: 我們可以通過(guò)長(zhǎng)度獲取到相應(yīng)長(zhǎng)度的數(shù)據(jù),可以很好解決粘包問(wèn)題
[socket readDataToLength: datalength withTimeout: -1  tag: 0];

Tag參數(shù)了解

CocoaAsyncSocket中的tag參數(shù)是不通過(guò)套接字發(fā)送的审孽,也不是從套接字讀取的县袱。tag參數(shù)只需通過(guò)各種委托方法回顯給您。它旨在幫助簡(jiǎn)化委托方法中的代碼佑力。

  [socket writeData: data1 withTimeout: - 1  tag: 1 ];
  [socket writeData: data2 withTimeout: - 1  tag: 2 ];
//  當(dāng)我們發(fā)送數(shù)據(jù)時(shí)候使用 tag 標(biāo)記后式散,發(fā)送后可以在委托方法中根據(jù) tag 看到那條數(shù)據(jù)已經(jīng)發(fā)送出去了。
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag :( long)tag{
    if(tag == 1)
         NSLog(@"First request sent ");
    else  if(tag == 2)
         NSLog(@"Second request sent ");
}

在讀取時(shí)數(shù)據(jù)時(shí)打颤,tag 也很有幫助:

[socket readDataWithTimeout:-1 tag:0];
// 讀取 tag 與上面方法的 tag 值是一一對(duì)應(yīng)的暴拄。
#define TAG_WELCOME 10
#define TAG_CAPABILITIES 11
#define TAG_MSG 12
- (void)socket:(GCDAsyncSocket *)sender didReadData :( NSData *)data withTag :( long)tag{
    if(tag == TAG_WELCOME)
    {
        //  忽略歡迎信息
    }
    else  if(tag == TAG_CAPABILITIES)
    {
        [self  processCapabilities:data];
    }
    else if (tag == TAG_MSG)
    {
        [self  processMessage: data];
    }
}

Tcp 粘包

1. 什么是tcp粘包?

TCP是面向連接的编饺,面向流的乖篷,提供高可靠性服務(wù)。收發(fā)兩端(客戶(hù)端和服務(wù)器端)都要有一一成對(duì)的socket透且,因此那伐,發(fā)送端為了將多個(gè)發(fā)往接收端的包,更有效的發(fā)到對(duì)方,使用了優(yōu)化方法(Nagle算法)罕邀,將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù)畅形,合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包诉探。這樣日熬,接收端,就難于分辨出來(lái)了,就會(huì)出現(xiàn)粘包現(xiàn)象

2. TCP粘包解決方案

目前應(yīng)用最廣泛的是在消息的頭部添加數(shù)據(jù)包長(zhǎng)度肾胯,接收方根據(jù)消息長(zhǎng)度進(jìn)行接收竖席;在一條TCP連接上,數(shù)據(jù)的流式傳輸在接收緩沖區(qū)里是有序的敬肚,其主要的問(wèn)題就是第一個(gè)包的包尾與第二個(gè)包的包頭共存接收緩沖區(qū)毕荐,所以根據(jù)長(zhǎng)度讀取是十分合適的;

2.1 解決發(fā)送方粘包

方案一: 發(fā)送產(chǎn)生是因?yàn)镹agle算法合并小數(shù)據(jù)包艳馒,那么可以禁用掉該算法憎亚;
方案二: TCP提供了強(qiáng)制數(shù)據(jù)立即傳送的操作指令push,當(dāng)填入數(shù)據(jù)后調(diào)用操作指令就可以立即將數(shù)據(jù)發(fā)送弄慰,而不必等待發(fā)送緩沖區(qū)填充自動(dòng)發(fā)送第美;
方案三: 數(shù)據(jù)包中加頭,頭部信息為整個(gè)數(shù)據(jù)的長(zhǎng)度(最廣泛最常用)陆爽;

//  `方案三`發(fā)送方解決粘包的代碼部分:
- (void)sendData:(NSData *)data{
        
        NSMutableData *sendData = [NSMutableData data];
        // 獲取數(shù)據(jù)長(zhǎng)度
        NSInteger datalength = data.length;
        //  NSInteger長(zhǎng)度轉(zhuǎn) NSData
        NSData *lengthData = [NSData dataWithBytes:&datalength length:sizeof(datalength)];
        // 長(zhǎng)度幾個(gè)字節(jié)和服務(wù)器協(xié)商好什往。這里我們用的是4個(gè)字節(jié)存儲(chǔ)長(zhǎng)度信息
        NSData *newLengthData = [lengthData subdataWithRange:NSMakeRange(0, 4)];
        // 拼接長(zhǎng)度信息
        [sendData appendData:newLengthData];
        //拼接數(shù)據(jù)
        [sendData appendData:data];
        // 發(fā)送加了長(zhǎng)度信息的包
        [self.clientSocket writeData:[sendData copy] withTimeout:-1 tag:0];
}

2.2解決接收方粘包
  1. 解析數(shù)據(jù)包頭部信息,根據(jù)長(zhǎng)度來(lái)接收慌闭;(最廣泛最常用)
/**
 數(shù)據(jù)緩沖區(qū)
 */
@property (nonatomic, strong) NSMutableData *dataBuffer;;

//  讀取客戶(hù)端發(fā)送的數(shù)據(jù),通過(guò)包頭長(zhǎng)度進(jìn)行拆包
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    
        //  數(shù)據(jù)存入緩沖區(qū)
        [self.dataBuffer appendData:data];
        
        // 如果長(zhǎng)度大于4個(gè)字節(jié)别威,表示有數(shù)據(jù)包。4字節(jié)為包頭驴剔,存儲(chǔ)包內(nèi)數(shù)據(jù)長(zhǎng)度
        while (self.dataBuffer.length >= 4) {
            
            NSInteger  datalength = 0;
            // 獲取包頭兔港,并獲取長(zhǎng)度
            [[self.dataBuffer subdataWithRange:NSMakeRange(0, 4)] getBytes:&datalength length:sizeof(datalength)];
            //  判斷緩存區(qū)內(nèi)是否有包
            if (self.dataBuffer.length >= (datalength+4)) {
                // 獲取去掉包頭的數(shù)據(jù)
                NSData *realData = [[self.dataBuffer subdataWithRange:NSMakeRange(4, datalength)] copy];
                // 解析處理
                [self handleData:realData socket:sock];
                
                // 移除已經(jīng)拆過(guò)的包
                self.dataBuffer = [NSMutableData dataWithData:[self.dataBuffer subdataWithRange:NSMakeRange(datalength+4, self.dataBuffer.length - (datalength+4))]];
            }else{
                break;
            }
        }
        [sock readDataWithTimeout:-1 tag:0];
}
  1. 自定義數(shù)據(jù)格式:在數(shù)據(jù)中放入開(kāi)始、結(jié)束標(biāo)識(shí)仔拟;解析時(shí)根據(jù)格式抓取數(shù)據(jù)衫樊,缺點(diǎn)是數(shù)據(jù)內(nèi)不能含有開(kāi)始或結(jié)束標(biāo)識(shí);
  2. 短連接傳輸利花,建立一次連接只傳輸一次數(shù)據(jù)就關(guān)閉科侈;(不推薦)

注:以上代碼僅提供粘包的解決思路,具體如何解包以及包頭數(shù)據(jù)結(jié)構(gòu)可以和服務(wù)器進(jìn)行商定

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炒事,一起剝皮案震驚了整個(gè)濱河市臀栈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挠乳,老刑警劉巖权薯,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姑躲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盟蚣,警方通過(guò)查閱死者的電腦和手機(jī)黍析,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屎开,“玉大人阐枣,你說(shuō)我怎么就攤上這事⊙俪椋” “怎么了蔼两?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)逞度。 經(jīng)常有香客問(wèn)我额划,道長(zhǎng),這世上最難降的妖魔是什么档泽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任俊戳,我火速辦了婚禮,結(jié)果婚禮上茁瘦,老公的妹妹穿的比我還像新娘品抽。我一直安慰自己储笑,他們只是感情好甜熔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著突倍,像睡著了一般腔稀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羽历,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天焊虏,我揣著相機(jī)與錄音,去河邊找鬼秕磷。 笑死诵闭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澎嚣。 我是一名探鬼主播疏尿,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼易桃!你這毒婦竟也來(lái)了褥琐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤晤郑,失蹤者是張志新(化名)和其女友劉穎敌呈,沒(méi)想到半個(gè)月后贸宏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡磕洪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年吭练,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐鸥。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡线脚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叫榕,到底是詐尸還是另有隱情浑侥,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布晰绎,位于F島的核電站寓落,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏荞下。R本人自食惡果不足惜伶选,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尖昏。 院中可真熱鬧仰税,春花似錦、人聲如沸抽诉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)迹淌。三九已至河绽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唉窃,已是汗流浹背耙饰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纹份,地道東北人苟跪。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蔓涧,于是被迫代替她去往敵國(guó)和親件已。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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

  • 一蠢笋、網(wǎng)絡(luò)各個(gè)協(xié)議:TCP/IP拨齐、SOCKET、HTTP等 網(wǎng)絡(luò)七層由下往上分別為物理層昨寞、數(shù)據(jù)鏈路層瞻惋、網(wǎng)絡(luò)層厦滤、傳輸層...
    杯水救車(chē)薪閱讀 2,268評(píng)論 0 17
  • 2016.7.4 今天晚上對(duì)項(xiàng)目頂層文件(daemon)進(jìn)行了分析,對(duì)其中的TCP連接進(jìn)行具體的代碼級(jí)分析歼狼。 1掏导、...
    zuoerfeng閱讀 1,875評(píng)論 0 4
  • 網(wǎng)絡(luò)編程 一.楔子 你現(xiàn)在已經(jīng)學(xué)會(huì)了寫(xiě)python代碼趟咆,假如你寫(xiě)了兩個(gè)python文件a.py和b.py,分別去運(yùn)...
    go以恒閱讀 2,024評(píng)論 0 6
  • 在開(kāi)發(fā)階段梅屉,隨著js框架和庫(kù)的引入值纱,頁(yè)面js的加載個(gè)數(shù)就越來(lái)越多,嚴(yán)重影響頁(yè)面的響應(yīng)速度坯汤。于是我們就需要對(duì)js和c...
    挨踢的菜鳥(niǎo)閱讀 875評(píng)論 0 0
  • 故事如下:惠能流浪到廣州法性寺虐唠,聽(tīng)見(jiàn)一僧道“風(fēng)吹幡動(dòng)”,又聽(tīng)一僧說(shuō)“幡動(dòng)而知風(fēng)吹”惰聂,惠能卻道:“非風(fēng)動(dòng)疆偿,非幡動(dòng),仁...
    月漸星闌閱讀 92評(píng)論 0 0