1、TCP基本认识
1.1 什么是TCP?
TCP是面向连接的、可靠的、基于字节流的 传输层 通信协议。
(基于IP,IP是不可靠的。TCP能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。 IP不能)
1.2 什么是TCP连接?
简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。
所以我们可以知道,建立一个 TCP 连接是需要客户端与服务端达成上述三个信息的共识。
- Socket:由 IP 地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
1.3 TCP头格式有哪些?
1字节=8位
1.4 如何确定一个TCP连接呢?
TCP 四元组可以唯一的确定一个连接。
1.5 如何在Linux中查看TCP状态?
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。
1.6 TCP和UDP的区别
补充:
1.TCP 传输数据前要先建立连接(三次握手)(传输数据完成后断开连接需要四次挥手);UDP是不需要连接,即刻传输数据
2.可靠性:TCP 保证数据传输的可靠性,通过序列号、确认应答和重传机制等方式来保证数据的完整性和正确性。UDP 则不保证数据传输的可靠性,因为它不提供确认和重传机制。
4.UDP 比 TCP 更快,因为它不需要建立连接和维护连接状态。UDP的实时性比TCP好
5.一对一的两点服务,即一条连接只有两个端点。
6.TCP 将数据看作是一连串的字节流,没有明确的消息边界。UDP 将数据看作是一系列的报文,每个报文是一个独立的单元,具有明确的消息边界。(TCP是流式传输,UDP是一个包一个包地发送,可能会丢包和乱序)
1.7 选择UDP而不选择TCP的原因
- 关于发送什么数据以及何时发送的应用层控制更为精细。(采用UDP时,只要应用进程将数据传递给UDP,UDP就会将此数据打包进UDP报文并立即将其传递给网络层)
- 无需建立连接。(DNS运行在UDP之上的主要原因。HTTP使用TCP而不是UDP是因为对于具有文本数据的web网页来说,可靠性很重要)
- 无连接状态。(UDP不维护连接状态,也不跟踪这些参数。因此某些出门用于某种特定应用的服务器当应用程序运行在UDP之上而不是TCP上时,一般能支持更多的活跃客户)
- 分组首部开销小。
2、TCP重传、滑动窗口、流量控制、拥塞控制
Tip1.TCP协议是如何保证可靠数据传输的?
- 确认机制
- 可靠重传
- 流量控制
- 拥塞控制
2.1 重传机制
TCP 实现可靠传输的方式之一,是通过序列号与确认应答。
但不一定能顺利地进行正常的数据传输,万一数据在传输过程中丢失了呢?TCP 针对数据包丢失的情况,会用重传机制解决。
常见的重传机制:(1)超时重传;(2)快速重传;(3)SACK;(4)D-SACK
✍超时重传
TCP 发生超时重传的两种情况:1)数据包丢失;2)确认应答丢失
超时重传时间的值应该略大于报文往返时间(数据发送时刻到接收到确认的时刻的差值)的值。
✍快速重传
超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?
于是就可以用「快速重传」机制来解决超时重发的时间等待。快速重传不以时间为驱动,而是以数据驱动重传。
(上图)快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
快速重传机制只解决了一个问题,就是超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传一个,还是重传所有的问题。
✍SACK方法(选择性确认)
为了解决不知道该重传哪些 TCP 报文,于是就有 SACK 方法。
这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
✍D-SACK
Duplicate SACK 又称 D-SACK,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
2.2 滑动窗口
滑动窗口是一种流量控制技术,用于控制数据包的发送和接收。(窗口的实现实际上是操作系统开辟的一个缓存空间)
滑动窗口协议可以有效地解决网络拥塞问题,同时也可以避免数据包的丢失和重复。
接收窗口的大小是根据接收端处理数据的速度动态调整的。
2.3 流量控制
发送方如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。
为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
TCP通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制。通俗地说,接收窗口用于给发送方一个指示——该接收方还有多少可用的缓存空间。因为TCP是全双工通信,在连接两端的发送方都各自维护一个接收窗口。
2.4 拥塞控制
流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。
一般计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大....
于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。 当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。
拥塞控制主要是四个算法(有的说四个阶段):
- 慢启动
- 拥塞避免
- 拥塞发生
- 快速恢复
3、TCP半连接队列和全连接队列
服务端收到客户端发出的 SYN 请求后,会把这个连接信息存储到半连接队列 (SYN 队列)。
服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到全连接队列 (accept 队列),等待进程调用 accept 函数时把连接取出来。
这两个队列都是有大小限制的,当超过容量后就会将链接丢弃,或者返回 RST 包。
4、TCP拆包
什么是拆包?
发生TCP拆包的原因:
- 待发送数据大于最大报文长度,TCP 在传输前将进行拆包。
- 发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包。
传输层封包不能太大,基于这个限制,往往以缓冲区大小为单位,将数据拆分成多个 TCP 段(TCP Segment)传输。在接收数据的时候,一个个 TCP 段又被重组成原来的数据。简单来讲分为几个过程:拆分——传输——重组。
解决方案:
- 发送端给每个数据包添加包首部,首部中包含数据包的长度,这样接收端在接收到数据后,通过该字段就可以知道每个数据包的实际长度了。
- 发送端将每个数据包设置固定长度,这样接收端每次从读取固定长度的数据把每个数据包拆分开。
- 可以在数据包之间设置边界,如添加特殊符号,接收端可以通过这个特殊符号来拆分包。
5、TCP粘包
5.1 什么是粘包?
在使用 TCP 传输数据时,才有粘包或者拆包现象发生的可能。一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
粘包是为了解决数据太小问题,防止多次发送占用资源。TCP 协议将它们合并成一个 TCP 段发送,在目的地再还原成多个数据。
5.2 TCP粘包是怎么产生的?
- 发送方产生粘包
采用 TCP 协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘包),双方在连接不断开的情况下,可以一直传输数据。但当发送的数据包过于的小时,那么 TCP 协议默认的会启用 Nagle 算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了。
- 接收方产生粘包
接收方采用 TCP 协议接收数据时的过程是这样的:数据到接收方,从网络模型的下方传递至传输层,传输层的 TCP 协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C 语言用 recv、read 等函数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包。(放数据的速度 > 应用层拿数据速度)
tips:UDP 没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是 UDP 报文或用户数据报,发送的时候既不合并,也不拆分。
5.3 TCP粘包现象处理方法
粘包的问题出现是因为不知道一个用户消息的边界在哪,如果知道了边界在哪,接收方就可以通过边界来划分出有效的用户消息。一般有三种方式分包的方式:
- 固定长度的消息;
- 特殊字符作为边界;
- 自定义消息结构。