美文网首页
粘包、半包

粘包、半包

作者: 一个半吊子工程师 | 来源:发表于2020-10-23 21:17 被阅读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];
    }   
    

    相关文章

      网友评论

          本文标题:粘包、半包

          本文链接:https://www.haomeiwen.com/subject/xxjbmktx.html