粘包、半包

2020-10-23  本文已影响0人  一个半吊子工程师

1.什么是粘包?

粘包通常出现在TCP的协议里面,对于UDP来说是不会出现粘包状况的。通俗的讲,就是在发包的时候会建立一个缓存区,发送的数据都会先进入这个缓存区,当上一条数据的接收被确认或者到达最大等待时间之后,才会将缓存区的数据一块发送过去,如此反复。将小包进行整合,避免小包多次发送造成的传输速度慢等问题。


image.png

2.什么时候才需要处理粘包?

理论上来讲,只要是基于TCP的socket链接,都需要处理粘包的情况。
  可能有些人在测试的时候并没有出现粘包的情况,认为并不需要对粘包进行处理,这种想法是错误的。

3.粘包解决方案

对于粘包,在Server端和Client端都需要解决粘包的问题,由于问题一致,所以解决方案也基本是通用的,只是相关语法需要变换一下。

使用了一个名为CocoaAsyncSocket的第三方

导入成功后,正式开始处理粘包问题。

首先,跟服务器确定数据头的问题

对于粘包,一般有两种解决方案:

在链接成功之后,在回调方法里面创建一个缓存区

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    self.readBuf = [[NSMutableData alloc] init];
  NSLog(@"链接成功后的其他操作");

}

然后在收到服务器发送的数据的时候,对这个数据进行处理

//服务器发送的数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    //将数据存入缓存区
    [self.readBuf appendData:data];
    
    //数据中前面有4个字节的头信息,其中前两位是固定的头长度(用处不大),后两位才是数据的长度。
    //如果大于4个字节证明有消息,因为服务器只要发送数据,必定包含头
    while (self.readBuf.length > 4) {
        
        //将消息转化成byte,计算总长度 = 数据的内容长度 + 前面4个字节的头长度
        Byte *bytes = (Byte *)[self.readBuf bytes];
        NSUInteger allLength = (bytes[2]<<8) + bytes[3] +4;
        
        //缓存区的长度大于总长度,证明有完整的数据包在缓存区,然后进行处理
        if (self.readBuf.length >= allLength) {
            NSMutableData *msgData = [[self.readBuf subdataWithRange:NSMakeRange(0, allLength)] mutableCopy];
            //提取出前面4个字节的头内容,之所以提取出来,是因为在处理数据问题的时候,比如data转json的时候,头内容里面包含非法字符,会导致转化出来的json内容为空,所以要先去掉再处理数据问题
            [msgData replaceBytesInRange:NSMakeRange(0, 4) withBytes:NULL length:0];
    
            NSLog(@"开始处理数据问题");

            //处理完数据后将处理过的数据移出缓存区
            _readBuf = [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(allLength, _readBuf.length - allLength)]];
        }else{
            //缓存区内数据包不是完整的,再次从服务器获取数据,中断while循环
            [self.clientSocket readDataWithTimeout:-1 tag:0];
            break;
        }
    }
    
    //读取到服务端数据值后,能再次读取
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}   
上一篇下一篇

猜你喜欢

热点阅读