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

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

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

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


iOS Socket编程:传输数据的封包,接收数据后半包、粘包、拆包处理_第1张图片
图片.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网络协议进行网络数据传输的同学有帮助,谢谢阅读。

你可能感兴趣的:(iOS Socket编程:传输数据的封包,接收数据后半包、粘包、拆包处理)