zoukankan      html  css  js  c++  java
  • Socket封包、粘包、拆包

    socket,如果是做tcp连接,可能会遇到粘包与半包的问题,TCP属于传输层的协议,传输层除了有TCP协议外还有UDP协议。那么UDP是否会发生粘包或拆包的现象呢?答案是不会。UDP是基于报文发送的,从UDP的帧结构可以看出,在UDP首部采用了16bit来指示UDP数据报文的长度,因此在应用层能很好的将不同的数据报文区分开,从而避免粘包和拆包的问题。而TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;另外从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段,在使用TCP传输数据时,才有粘包或者拆包现象发生的可能。粘包就是多组数据被一并接收了,粘在了一起,无法做划分;半包就是有数据接收不完整,无法处理。要解决粘包、半包的问题,一般在设计数据(消息)格式时会约定好一个字段专门用于描述数据包的长度,这样就使数据有了边界,依靠这个边界,就能把每组数据划分出来,数据不完整时也能获知数据的缺失。

    封包

    一般在使用Socket的时候,后台会对Socket传输数据有一个自定义的协议,协议可能有些差别不过基本上是大同小异。

    Socket发送给服务器的数据,最终要转换成二进制流数据,并且按照协议约定的格式。

    eg:消息=消息头+消息体。消息头用于描述消息本身的基本信息,消息体则为消息的具体内容

    上面这个协议是指我们在发送的数据包头部开辟一个4个字节长度的空间,用来存储服务号转换成的二进制数据。(将1转换成二进制数据存储进去占4个字节长度),然后再将数据包长度转换成二进制数据并存储到后面开辟的4个字节中(这里需要注意下如果数据要进行加密传输,这里的长度应是加密后的长度),最后将数据数据包转换成二进制数据添加到后面,组成一个完整的数据包也就是封包。这里一定要按协议规定的顺序不然服务器解析不了。

    粘包、拆包

    我们假设在主机A和主机B的应用程序之间有一条TCP连接,主机A有两条报文D1,D2要发送到B主机,并两次调用send来发送,每条报文调用一次。

    那么,我们自然而然的希望两条报文是作为两个独立的实体,在各自的分组中发送

    这样的话,我们无需做任何特别的处理,便能够很容易的区分每一个独立的数据,并根据需求分别做相应的处理。但现实往往是有所偏差的,实际的数据传输过程很可能不会遵循这个模型。而是会采用以下四种方式之一进行传输

    实际上,可能的情况还不止4种,这里我们就不做深入了解,以上就是造成粘包的原因。

    解决思路:拆包

    在上面说到我们给每个数据包添加头部,头部中包含数据包的长度,这样接收到数据后,通过读取头部的长度字段,便知道每一个数据包的实际长度了,再根据长度去读取指定长度的数据便能获取到正确的数据了。
    再来回顾一下 协议:

     

    完整的数据包 = 服务号 + 数据包长度 + 数据
    数据包头 = Id(4B) + length(4B) 共占用8字节
    数据包 = length(假设占100个字节)
    所以这条消息的长度就是108字节可以看到,要想知道一条完整数据的边界,关键就是数据包头中的length字段

    iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket

    iOS开发之Socket通信实战--Request请求数据包编码模块

    补充博客:初用 CocoaAsyncSocket

  • 相关阅读:
    do-while语句
    指针操作符
    字符译码
    PHP流程控制分支结构
    PHP数据类型和常量
    PHP中使用的变量
    第一个PHP程序
    HTML的区块属性
    HTML的定位属性
    HTML的盒子模型
  • 原文地址:https://www.cnblogs.com/baitongtong/p/12979277.html
Copyright © 2011-2022 走看看