欢送大家搜寻“小猴子的技术笔记”关注我的公众号,支付丰盛面试材料和学习材料。
你理解 TCP 缓冲区吗?它和 TCP 传输中的粘包和拆包有什么关系呢?粘包和拆包别离产生在 TCP 的那个阶段呢?
先简略回顾下 TCP 概念:在网络传输中 TCP 是面向连贯的、牢靠的、双通道、字节流一对一传输。TCP 单方通信必须要先建设连贯,而后调配必要的内核资源。单方替换结束数据之后必须都要断开连接用来开释系统资源,长链接能够不用断开连接复用同一个通道。那么什么是 TCP 的缓冲区呢?
操作系统中有两个空间:用户空间和内核空间。每一个 socket 连贯都是在内核空间,内核针对每一个 socket 都有一个发送缓冲区和接收缓冲区。TCP 的双工工作模式以及流量管制就是依赖这两个缓冲区的填充来实现的。
咱们之前用 socket 获取“OutputStream”获取一个输入流进行字节的写出,其实是写入到了“send buffer”发送缓冲区中,这个时候数据不肯定会发送到对方机器上。“write()”办法仅仅是将用户空间数据拷贝到了内核发送缓冲区中,具体什么时候发送由 TCP 决定。
TCP 会从发送缓冲区中把数据通过网卡发送到指标机器的内核缓冲区中。如果零碎始终没有调用 ”recv()” 办法进行读取的话,那么数据将会始终挤压在 socket 的 recv buffer 中。
TCP 粘包、拆包问题的由来:
如果你看懂了下面这幅图的话,那么对于粘包和拆包的问题就比拟好了解了。在这里我想先问一个问题,粘包和拆包是产生在传输过程中吗?
粘包和拆包问题到底产生在什么阶段?首先咱们须要分明地理解 TCP 数据是牢靠的,因而必定不是传输的过程中!因为数据发送是从缓冲区 -> 网卡,因而粘包问题是从缓冲区读取数据的时候产生的。拆包则是从缓冲区到网卡的阶段产生的。
这里先解释下粘包:所谓的粘包就是发送方在同一时刻收回了两个或者两个以上的包到接收端。
假如发送端须要发送两条数据“别缓和,你这样没事的!”和“好好看文章,你肯定能够学会”。首先会把这两条数据放到发送缓冲区中,而后在通过网卡进行数据的发送到接管方的接收缓冲区中。如果接管方没有及时从接收缓冲区中获取往外取数据,那么数据就会在缓冲区挤压,这样两条数据就会积压在一块,就成了一条数据,这就是粘包的问题!
那么什么是拆包问题呢?拆包问题是 TCP 每次发送的长度是有限度的,如果发送一个包的数据过大的话,TCP 就会把这个包拆成两个包来进行发送。
假如要发送的数据“别缓和,你这样没事的!”很大,TCP 在发送的时候把它拆成了“别缓和,你这样”和“没事的!”进行发送,那么在接管方就会收到两个报文,这就是拆包的问题。
实际上过大的话,还有可能会被拆成三个或者更多的包进行发送。然而无论被拆成几个包,TCP 都可能保障发送包的程序性和正确性。
那么产生粘包和拆包的起因是什么呢?这个和 TCP 的缓冲区与滑块窗口、MSS/MTU 限度、Nagle 算法无关。
有了粘包和拆包的问题,咱们在理论的开发中应该怎么防止或者解决这个问题呢?那就是定义咱们的通信协定。这样如果粘包了就能够依据协定来辨别不同的包,如果拆包了就期待数据形成一个残缺的音讯之后在进行解决。
第一种形式 — 定长协定:所谓的定长协定就是指定一个报文的固定长度,每次单方依照约定截取固定的长度。假如咱们须要发送“hello”和“very”两个单词,依照约定的 5 个字节进行一次截取。那么有余 5 个字节的单词能够增加 0 作为补充,则产生的规定如下。
因为有余约定长度的须要进行补 0,因而定长协定会造成带宽的节约。
第二种形式 — 特殊字符分隔符:应用特殊字符分隔符就是在报文的结尾进行追加特殊字符分隔符,用次分隔符来标注这是一个残缺的报文,例如遇到了“\n”。
这样尽管能够对报文进行划分,然而要求就是报文中不能蕴含非凡分隔符。
第三种形式 — 固定头长度:发送数据之前,须要先获取须要发送内容的二进制字节大小,而后在须要发送的内容后面增加一个固定长度头整数,示意音讯体二进制字节的长度。
这种形式防止了特殊字符带来的问题,是生产中能够采取的一个形式,我在之前的文章中有介绍过这样的应用办法。
其实对于 java 程序员来说,咱们不用过分关怀接管和发送缓冲区,须要理解其概念,因为底层曾经为咱们做了封装。明确“粘包”和“拆包”产生的过程和起因。
通过观察用户空间和内核空间的数据交互,你兴许会发现进行一次残缺的交互须要进行四次的数据拷贝,这在性能上可能会有所影响。这也就有了面试官常常问的“零拷贝”的问题,尝试着本人对本文的了解学习一下“零拷贝”,这是为前面学习 Netty 打下松软的根底。