iOS Socket编程:传输数据的封包,接收数据后半包、粘包、拆包处理

1 客户端与服务端的连接规则

一般客户端与服务端要实现TCP连接,要遵循一定的规则:比如【header字节数组 + Body字节数组 】组成要发送的数据。

其中header数组一般由服务端和客户端的传输协议,用来进行数据的解析。比如下面的header传输协议


图片.png

下面是传输数据封包代码:

static uint const kSocketVersion = 1; //当前Socket版本
static uint const kSocketFlag = 1;
static int  const kHeaderLength = 36;//sokect头部预留长度

-(void)sendDataWithMainType:(ushort)mainType subType:(ushort)subType andBody:(NSString *)body {

    /** 后台约定的编码格式 */
    NSStringEncoding GBKEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
    NSData *bodyData = [body dataUsingEncoding:GBKEncoding];

    NSString *lenght = [NSString stringWithFormat:@"%ld",bodyData.length];
    int num = [lenght intValue];
    Byte buffData[20];
     /** 数据拼接 */
    NSMutableData *totalData = [[NSMutableData alloc] init];
    NSData *bodyLenghtData = [NSData dataWithBytes:&num length:4];//消息体长度字节数组数据
    NSData *mtypeData = [NSData dataWithBytes:&mainType length:2];//主类型字节数组数据
    NSData *stypeData = [NSData dataWithBytes:&subType length:2];//子类型字节数组数据
    NSData *version = [NSData dataWithBytes:&kSocketVersion length:4];//版本
    NSData *flag = [NSData dataWithBytes:&kSocketFlag length:4];//标识
    NSData *bufferData = [NSData dataWithBytes:buffData length:20];//预留

    [totalData appendData:bodyLenghtData];
    [totalData appendData:mtypeData];
    [totalData appendData:stypeData];
    [totalData appendData:version];
    [totalData appendData:flag];
    [totalData appendData:bufferData];
    [totalData appendData:bodyData];

    [self.socket writeData:totalData withTimeout:-1 tag:kTag];
}

2.Socket数据的接收

接收到服务端返回的数据后,除了需要拆包提取出包里面的数据外,由于数据包大小的问题,还有可能出现半包、粘包等问题。下面是接收数据后的处理代码:

//接收信息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
  
   [self.completeData appendData:data];
   /** 递归方式读取返回的数据 先判断返回数据是否大于头部长度,如果没有直接加入缓存 */
    while (self.completeData.length >= kHeaderLength) {

        /** 拆包 */
        NSData *header = [self.completeData subdataWithRange:NSMakeRange(0, 36)];
        NSData *bodyLengthData = [header subdataWithRange:NSMakeRange(0, 4)];
        NSData *mainTypeData = [header subdataWithRange:NSMakeRange(4, 2)];
        NSData *subTypeData = [header subdataWithRange:NSMakeRange(6,2)];

        [mainTypeData getBytes:&_curMainType length:2];
        [subTypeData getBytes:&_curSubType length:2];
        [bodyLengthData getBytes:&_curBodyDataLength length:4];

        NSInteger completeDataLength = _curBodyDataLength + kHeaderLength;//完整包长度(内容长度+头长度)
        /** 判断是否是够完整包 */
        if (self.completeData.length >= completeDataLength)
        {
            NSData *curData = [self.completeData subdataWithRange:NSMakeRange(0, completeDataLength)];//截取一个包的长度(处理粘包)
            [self handleSocketResponseData:curData];//处理包数据
            self.completeData = [NSMutableData dataWithData:[self.completeData subdataWithRange:NSMakeRange(completeDataLength, self.completeData.length - completeDataLength)]];
        }else
        {
            [_socket readDataWithTimeout:-1 buffer:self.completeData bufferOffset:self.completeData.length tag:0];//继续读取数据
            return;
        }
    }
     [_socket readDataWithTimeout:-1 buffer:self.completeData bufferOffset:self.completeData.length tag:0];//继续读取数据
}

-(void)handleSocketResponseData:(NSData *)data{

    NSData *bodyData = [data subdataWithRange:NSMakeRange(36, [data length] -kHeaderLength)];
    NSString *msg = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];

    if (_curSubType != kSOCKETMSGTYPE_HEARTBEAT && ![[NSString replaceNullValue:msg] isEqualToString:@""]) {

        DDLogDebug(@"\n接收到的消息体 = %@\n接收到的主类型 = %d\n接收的子类型 = %d\n",msg,_curMainType,_curSubType);
        IDSocketRequestInfo *requestInfo = [[IDSocketRequestInfo alloc] init];
        requestInfo.requestBody = msg;
        requestInfo.requestMainType = _curMainType;
        requestInfo.requestSubType = _curSubType;
        [[NSNotificationCenter defaultCenter] postNotificationName:kSocketDidRecivedDataNotification object:requestInfo userInfo:nil];
    }
}

希望文章对有用Socket网络协议进行网络数据传输的同学有帮助,谢谢阅读。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容