1.什么是粘包?

  粘包通常出现在TCP的协议里面,对于UDP来说是不会出现粘包状况的,之所以出现这种状况的原因,涉及到一种名为Nagle的算法。

  Nagle算法通过减少必须发送的封包的数量,提高网络应用程序系统的效率,解决负载问题。通俗的讲,就是在发包的时候会建立一个缓存区,发送的数据都会先进入这个缓存区,当上一条数据的接收被确认或者到达最大等待时间之后,才会将缓存区的数据一块发送过去,如此反复。将小包进行整合,避免小包多次发送造成的传输速度慢等问题。

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

  理论上来讲,只要是基于TCP的socket链接,都需要处理粘包的情况。

  可能有些人在测试的时候并没有出现粘包的情况,认为并不需要对粘包进行处理,这种想法是错误的。

  首先,在测试的时候之所以没有出现粘包情况,极有可能是因为网络路由问题,导致TCP的MTU会有所变化,Internet上的标准MTU值为576,以太网MTU为1500。所以测试粘包一般以外网环境进行测试。

3.粘包解决方案

  由于Nagle算法已经成为了默认的执行方式,所以对于粘包,在Server端和Client端都需要解决粘包的问题,由于问题一致,所以解决方案也基本是通用的,只是相关语法需要变换一下。

  由于iOS的底层对于socket的封装并不是那么完善,使用的话还得采用C语言,因此使用了一个名为CocoaAsyncSocket的第三方,将C的socket封装成了适合OC面向对象的形式,github地址如下:https://github.com/robbiehanson/CocoaAsyncSocket

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

  首先,跟服务器确定数据头的问题,对于粘包,一般有两种解决方案,第一种就是服务器返回的字段中有可识别的头和尾,我们可以根据可识别的头和尾来拆包。第二种是服务器返回的数据只包含头,头里面有数据的长度,我们可以根据这个头包含的数据长度来进行拆包。本文采用的便是第二种方案。

  服务器返回的数据为data形式的数据,打印出来的数据如下:

<fefd00f9 7b22736e 223a342c 22766572 73696f6e 223a2231 2e30222c 226e6574 466c6167 223a312c 22636d64 54797065 223a312c 22706475 223a7b22 70647554 79706522 3a343039 382c2264 65764461 7461223a 5b7b2264 65764964 223a2231 32333435 36373822 2c226465 764e616d 65223a22 e59b9ee8 b7afe68e a7e588b6 222c2270 4964223a 22343039 3833222c 22706172 616d223a 5b7b2274 79706522 3a383232 372c2276 616c7565 223a317d 2c7b2274 79706522 3a383232 382c2276 616c7565 223a307d 2c7b2274 79706522 3a383232 392c2276 616c7565 223a317d 2c7b2274 79706522 3a383233 302c2276 616c7565 223a317d 5d7d5d7d 7d>

  其中fefd00f9为服务器返回的头,后面为具体的数据。其中fefd占两个字节,00f9占两个字节,fefd是固定的值不做任何处理,00f9就是数据的长度

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

- (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 > ) { //将消息转化成byte,计算总长度 = 数据的内容长度 + 前面4个字节的头长度
Byte *bytes = (Byte *)[self.readBuf bytes];
NSUInteger allLength = (bytes[]<<) + bytes[] +; //缓存区的长度大于总长度,证明有完整的数据包在缓存区,然后进行处理
if (self.readBuf.length >= allLength) {
NSMutableData *msgData = [[self.readBuf subdataWithRange:NSMakeRange(, allLength)] mutableCopy];
//提取出前面4个字节的头内容,之所以提取出来,是因为在处理数据问题的时候,比如data转json的时候,头内容里面包含非法字符,会导致转化出来的json内容为空,所以要先去掉再处理数据问题
[msgData replaceBytesInRange:NSMakeRange(, ) withBytes:NULL length:]; NSLog(@"开始处理数据问题"); //处理完数据后将处理过的数据移出缓存区
_readBuf = [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(allLength, _readBuf.length - allLength)]];
}else{
//缓存区内数据包不是完整的,再次从服务器获取数据,中断while循环
[self.clientSocket readDataWithTimeout:- tag:];
break;
}
} //读取到服务端数据值后,能再次读取
[self.clientSocket readDataWithTimeout:- tag:];
}

  这样处理之后,可以将粘包问题解决

  

iOS 处理socket粘包问题的更多相关文章

  1. Socket粘包问题

    这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...

  2. C#下利用封包、拆包原理解决Socket粘包、半包问题(新手篇)

    介于网络上充斥着大量的含糊其辞的Socket初级教程,扰乱着新手的学习方向,我来扼要的教一下新手应该怎么合理的处理Socket这个玩意儿. 一般来说,教你C#下Socket编程的老师,很少会教你如何解 ...

  3. [转]关于Socket粘包问题

    这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...

  4. 解决Socket粘包问题——C#代码

    解决Socket粘包问题——C#代码 前天晚上,曾经的一个同事问我socket发送消息如果太频繁接收方就会有消息重叠,因为当时在外面,没有多加思考 第一反应还以为是多线程导致的数据不同步导致的,让他加 ...

  5. Python socket粘包解决

    socket粘包: socket 交互send时,连续处理多个send时会出现粘包,soket会把两条send作为一条send强制发送,会粘在一起. send发送会根据recv定义的数值发送一个固定的 ...

  6. TCP Socket 粘包

     这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题.发现自己不是非常清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接: 1.长连接 Client方与Server ...

  7. socket粘包现象加解决办法

    socket粘包现象分析与解决方案 简单远程执行命令程序开发(内容回顾) res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=su ...

  8. 百万年薪python之路 -- socket粘包问题解决

    socket粘包问题解决 1. 高大上版解决粘包方式(自定制包头) 整体的流程解释 整个流程的大致解释: 我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小啊之类的),然后json序 ...

  9. Socket粘包问题终极解决方案—Netty版(2W字)!

    上一篇我们讲了<Socket粘包问题的3种解决方案>,但没想到评论区竟然炸了.介于大家的热情讨论,以及不同的反馈意见,本文就来做一个扩展和延伸,试图找到问题的最优解,以及消息通讯的最优解决 ...

随机推荐

  1. JDBC连接池-C池3P0连接

    JDBC连接池-C3P0连接 c3p0连接池的学习英语好的看英文原版      c3p0 - JDBC3 Connection and Statement Pooling 使用c3p0连接池  三种方 ...

  2. 一个基于EntityFramework Core的简单数据库访问层,适用于轻量级数据库业务

    这个访问层的代码实际上是园子里某个前辈的,本人只是觉得好使,记录了下来. 本访问层需要通过Nuget安装EntityFramework Core,不过个人认为EF 6同样可以使用. 搭配数据库,最好是 ...

  3. Luogu 2296 寻找道路

    https://www.luogu.org/problemnew/show/2296 题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以 ...

  4. 一些神奇的JS功效

    1: 沉睡排序 var numbers=[1,2,3,4,5,5,99,4,20,11,200]; numbers.forEach((num)=>{ setTimeout(()=>{ co ...

  5. C# 面向对象基础&封装&继承&多态&加深一下冒泡排序写法

    (一)面向对象是什么? 面向对象是一种编程思想 (二)为什么要用面向对象? 1.结构清晰 2.易于维护 3.方便扩展 (三)new一个对象是什么过程? 实例化构造函数创建对象的过程就是将类实例化的过程 ...

  6. 将非常规Json字符串转换为常用的json对象

    如下所示,这是一个已经转换为Json对象的非常规Json字符串,原来是一个Json类型的字符串,在转换为Json对象时,查询资料发现有两种转换法,.parse()和.eval()方法,但是前辈们都极其 ...

  7. C#算法面试题

    1.产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复. static void GetArray() { ]; ]; ; i < ; i++) { intArr[i] ...

  8. c++ const全局对象是如何处理的

    我主要是记录一个发现,目前我不能解释,先作个记录. const 只是一个 语义约束,由编译器强制实施的.使被约束的对象不能被直接访问修改. 我用 『直接』这词,因为在代码段中 ,用一个const 指针 ...

  9. Logstash&Redis&Elasticsearch&Kibana

    [搭建] 一个很好的提示,强调版本的一致性 http://www.cnblogs.com/yjf512/p/4194012.html http://michael.bouvy.net/blog/en/ ...

  10. springMVC初探

    MVC框架要做哪些事情? a,将url映射到java类,或者java类的方法上 b,封装用户提交的数据 c,处理请求->调用相关的业务处理—>封装响应的数据 d,将响应的数据进行渲染 sp ...