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];
}
网友评论