CocoaAsyncSocket 学习(二) 数据粘包、断包处理实例

原文参考:https://www.jianshu.com/p/2e16572c9ddc

一、什么是粘包?

经常我们发现,如果用客户端同一时间发送几条数据,而服务端只能收到一大条数据

TCP 数据传输为什么会造成粘包呢?

原来这是因为TCP使用了优化方法(Nagle算法)。
它将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。
这么做优点也很明显,就是为了减少广域网的小分组数目,从而减小网络拥塞的出现。

而UDP就不会有这种情况,它不会使用块的合并优化算法。
这里说到了就顺便提一下,由于它支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息)。

当然除了优化算法,TCP和UDP都会因为下面两种情况造成粘包:
发送端需要等缓冲区满才发送出去,造成粘包
接收方不及时接收缓冲区的包,造成多个包接收。

二、什么是断包?

断包应该还是比较好理解的,比如我们发送一条很大的数据包,类似图片和录音等等,很显然一次发送或者读取数据的缓冲区大小是有限的,所以我们会分段去发送或者读取数据。

三、实例:基于CocoaAsyncSocket的封包,拆包处理。

开始动手之前,我们需要去理解下面这几个方法

//读取数据,有数据就会触发代理
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
//直到读到这个长度的数据,才会触发代理
- (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
//直到读到data这个边界,才会触发代理
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

CocoaAsyncSocket 这个框架每次读取数据,必须手动的去调用上述这些read方法,而我们之前的实现思路是,第一次连接成功的代理触发后调用:

- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;

之后每次收到消息之后,都在去调用一次这个方法,超时为-1,即不超时。这样我们每次收到消息,都会即时触发我们读取消息的代理:

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

然而这么做显然没有考虑数据的拆包,如果我们一条一条的发送文字信息,自然没什么问题。如果我们一次发送数条,或者发送大图片。那么问题就出来了,我们解析出来的数据显然是不对的。

这时候我们就需要另外两个read方法了,一个是读取到指定长度,另一个是读取到指定边界。

我们通过自己定义的数据边界,去调用这两个方法,而触发的读取代理,得到的数据才是正确的一个包的数据。

所以我们的核心思路有了:

  • 1、封包的时候给每个包的数据加一个标记,来标明数据的长度和类型(类型显然是需要的,我们需要知道它是文本、图片、还是录音等等,来用正确的方式处理这个数据)。

  • 2、拆包的时候,先获取到我们给每个包的标记,然后根据标记的数据长度,去获取数据。最后再根据标记的类型去处理数据。(文字输出、图片展示、录音播放等等)。

[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];

还记得我们封包的时候,数据包头部之后拼了这么一个分解符`data`。这样,当有数据包传输过来我们就能获取到这个数据包的头部(后面的信息先不读取)。

接着我们来看看服务端的`read`代理方法是如何拆包的:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容