iOS 使用GCDAsyncSocket及粘包、半包處理

iOS開發(fā)中可以使用開源庫CocoaAsyncSocket簡化socket開發(fā)

**1.連接socket **

//創(chuàng)建一個TCP服務 連接到服務器
- (void)createTcpSocket {
    if (self.asyncSocket==nil) {
        self.asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    if (self.asyncSocket.isConnected) {
    } else {
        NSError *error;
        [self.asyncSocket connectToHost:_socketHost onPort:_socketPort withTimeout:-1 error:&error];
        if (error) {
            NSLog(@"%@",error);
        }
    }
}

2.實現(xiàn)socket代理方法

//已經連接到服務器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"連接成功");
}
// 連接斷開
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
   NSLog(@"連接斷開");
}
//消息發(fā)送成功 代理函數(shù) 向服務器 發(fā)送消息
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"消息發(fā)送成功");
}
//已經接收服務器返回來的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { 
    NSLog(@"接收到服務器返回的數(shù)據(jù)");
}

3.向服務器發(fā)送消息

[self.asyncSocket writeData:self.message withTimeout:-1 tag:0];

以上只是簡單的介紹CocoaAsyncSocket的使用,下面結合實際開發(fā)中的需求來講解開發(fā)中的關鍵點,比如 tag 用來干嘛???

在開發(fā)中前端和后端會約定一個固定的數(shù)據(jù)(消息)格式,按照這個格式來讀取數(shù)據(jù),就能把每組數(shù)據(jù)劃分出來,也就較好的解決了 粘包 掉包 的問題,數(shù)據(jù)不完整時也能獲知數(shù)據(jù)的缺失。

舉個栗子,如下表格是規(guī)定好的消息格式
起始符 目標地址 原地址 數(shù)據(jù)長度 發(fā)送/接收 的數(shù)據(jù) 檢驗符
0x02(1Byte) dest(2Byte) src(2Byte) dataLength(2Byte) data (數(shù)據(jù)) CRC校驗 (1Byte)

注:以上的消息格式分成3塊,數(shù)據(jù)和校驗符在這里稱為身體部分
1.消息頭部:起始符 + 目標地址 + 原地址 + 數(shù)據(jù)長度 = 7Byte,(頭部包含了那么多信息,并且長度是固定的,所以我們接收消息的時候,要先從頭部開始入手)
2.消息體:要發(fā)送或者接收到的數(shù)據(jù),長度為dataLength
3.校驗符:用來檢驗接收的數(shù)據(jù)是否完整一致(開發(fā)中可能沒有)

** 為了能夠在收到消息時,先獲取到頭部信息 ,我們需要用到 tag **

//固定的頭部長度
//起始符(1Byte) + 目標地址(2byte) + 源地址(2byte) + 應用層數(shù)據(jù)長度(2byte) = 7Byte
#define KPacketHeaderLength 7
typedef NS_ENUM(NSInteger ,KReadDataType){
    TAG_FIXED_LENGTH_HEADER = 10,//消息頭部tag
    TAG_RESPONSE_BODY = 11//消息體tag
};

** 所以在socket連接成功之后應該這樣寫 **

//已經連接到服務器
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
    NSLog(@"連接成功");
    // -1不超時一直讀取 等待數(shù)據(jù),先讀取頭部信息長度為 KPacketHeaderLength
    // tag為頭部消息tag,這個在接收到數(shù)據(jù)時,用來區(qū)分此次讀取的是頭部數(shù)據(jù)還是消息體數(shù)據(jù)
    [self.asyncSocket readDataToLength:KPacketHeaderLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER];
}

** 在接受到服務器的數(shù)據(jù)時 **

//已經接收服務器返回來的數(shù)據(jù)
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { 
    NSLog(@"接收到服務器返回的數(shù)據(jù)");
    // 根據(jù)tag來做不同的操作
    switch (tag) {
        case TAG_FIXED_LENGTH_HEADER:
        {
            self.headData = data;
            Byte *bytes = (Byte *)[data bytes];

            //身體長度 = 消息體 + 1Byte的校驗碼
            self.bodyLength =  [self readShort:bytes location:5] + 1;

            // 從數(shù)據(jù)緩沖區(qū)讀取完整的身體部分數(shù)據(jù),此時tag變成了TAG_RESPONSE_BODY
            [self.asyncSocket readDataToLength:self.bodyLength withTimeout:-1 tag:TAG_RESPONSE_BODY];
        }
            break;
        case TAG_RESPONSE_BODY:{

            //如果當前讀取出來的數(shù)據(jù)長度沒有達到完整包身體的長度,則包不完整(則根據(jù)當前接收的數(shù)據(jù)長度,和身體長度比較,繼續(xù)讀取兩者相差的數(shù)據(jù)長度)

            //讀取完身體數(shù)據(jù),開始校驗,校驗成功,則展示數(shù)據(jù)并且,開始等待下一次讀取數(shù)據(jù),tag變成TAG_FIXED_LENGTH_HEADER
            [self.asyncSocket readDataToLength:KPacketHeaderLength withTimeout:-1 tag:TAG_FIXED_LENGTH_HEADER];
        }
            break;
        default:
            break;
    }
}

- (short)readShort:(Byte *)bytes location:(int)location {
    return OSReadLittleInt16(bytes, location);
}

這些只是開發(fā)中可能會遇到的點,實際中還有很多細節(jié)要處理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 轉載:http://www.cocoachina.com/ios/20170615/19529.html 參考:h...
    F麥子閱讀 4,136評論 3 2
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • iPhone的標準推薦是CFNetwork 庫編程,其封裝好的開源庫是 cocoa AsyncSocket庫,用它...
    Ethan_Struggle閱讀 2,357評論 2 12
  • 經常在書中看到,許多人因為感情不能鐘情眷屬,而在一起的婚姻,又有多少不是因為愛情。 我,27歲,已婚。 我出生在一...
    揚揚灑灑閱讀 159評論 0 0
  • 圣經中沒有記載耶穌此日的言行。在這一天里,主耶穌什么都沒有去做,祂把自己擺在父神的面前,靜心靈修,藉著與父...
    木木_77閱讀 3,212評論 0 1

友情鏈接更多精彩內容