概念:
TCP是面向连接的 可靠的 字节流式服务
- 面向连接:是指发送数据之前必须在两端建立连接。建立连接的方法式“三次握手”,这样能建立可靠的连接。建立连接,是为了数据的可靠传输打下基础。
- 可靠:通过三次握手、序列号与确认应答机制、超时重传机制、滑动窗口机制、拥塞控制机制以及数据校验等操作来保证可靠性
- 仅支持单播传输:每条TCP传输连接只能有两个端点,只能进行点对点的数据传输。不支持多播和广播的传输方式
- 字节流:TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据。TCP作为传输层协议并不了解上层业务的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包发送。也有可能把多个小包封装成一个大包发送。这就是所谓TCP粘包和拆包问题。
什么是TCP粘包?如何解决?
正是TCP字节流的特性导致了此问题。
举个例子来直观说明:发送方准备发送 「Hi.」和「I am Xiaolin」这两个消息。
在发送端,当我们调用send函数完成数据“发送”以后,数据并没有真正从网络上发送出去,只是从应用程序拷贝到了操作系统内核协议栈中。至于什么时候真正被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。也就是说,我们不能认为每次 send 调用发送的数据,都会作为一个整体完整地消息被发送出去。
如果我们考虑实际网络传输过程中的各种影响,假设发送端陆续调用 send 函数先后发送 「Hi.」和「I am Xiaolin」 报文,那么实际的发送很有可能是这几种情况。
我们在考虑一个问题:
TCP中有一个negal算法,用途是这样的:通信两端有很多小的数据包要发送,虽然传送的苏数据很少,但该走的流程一样没少,也需要TCP各种确认,校验。如果这样的小数据包很多,会造成网络资源的浪费,negal算法做了这样一件事,当来了一个很小的数据包,我们不急于发送他,而是等来了更多的包,将这些小包合成一个大包后一并发送,这样不就提高了网络传输效率了吗。但这样也造成了一个问题,如果是分成两个不同页面的包,被合并在了一起,那么客户那边如何区分呢?这就是粘包
在这里引出两个概念:
长连接:Client和Server方先建立通讯连接,连接建立后不断开,然后在进行数据的发送和接受
短连接:Client和Server每进行一次数据收发交易时才进行通讯的连接,交易完毕后立即断开连接。
处理拆包提供两种方法:
- 通过包头+包长+包体的协议形式,当服务器获取到指定的包长时才说明获取完整
- 指定包结束标识,这样当我们获取到指定的标识时,说明包获取完整
处理粘包,虽然像http这样的短连接不会出现粘包现象,但一旦建立的长连接,粘包问题还是会发生的,处理方式如下:
- 发送方对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法
- 将问题交给应用层来解决。方法:循环处理,应用程序从接受缓存中读取分组时,读完一条数据,就循环读取下一条数据,直到所有数据都被处理完成,判断每条数据的长度方法有两种:
(1)格式化数据:每条数据有固定的格式(如开始符,结束符),但要确保每条数据的内部不包含开始符和结束符。
(2)发送长度:每条数据发送时,将数据长度一并发送。
TCP的可靠性
TCP 协议保证数据传输可靠性的方式主要有:校验和、序列号、确认应答、超时重传、连接管理、流量控制、拥塞控制。
1. 序列号和确认应答:
TCP 通过给每个发送的数据段分配一个序列号,以及使用确认(ACK)机制来跟踪数据的传输和接收。接收方会确认已成功接收的数据,发送方则根据收到的确认来确定哪些数据已经被成功传输,以及哪些需要重新发送。
2. 超时和重传:
TCP 使用超时机制来检测是否发生了数据包的丢失。如果发送方在一定时间内未收到确认,它会认为数据包丢失,并触发相应的重传。这确保了即使某个数据包在传输过程中丢失,它最终仍能够被成功传递。
3. 流量控制:
TCP 使用滑动窗口机制来进行流量控制,确保发送方不会以高于接收方处理速度的速率发送数据。这有助于防止接收方缓冲区溢出,并提高整个通信链路的效率。
- 滑动窗口大小:在 TCP 通信中,每个 TCP 报文段都包含一个窗口字段,该字段指示发送方可以发送多少字节的数据而不等待确认。这个窗口大小是动态调整的。
- 接收方窗口大小:接收方通过 TCP 报文中的窗口字段告诉发送方自己当前的可接收窗口大小。这是接收方缓冲区中还有多少可用空间。
- 流量控制的目标:流量控制的目标是确保发送方不要发送超过接收方缓冲区容量的数据。如果接收方的缓冲区快满了,它会减小窗口大小,通知发送方暂停发送,以防止溢出。
- 动态调整:发送方会根据接收方的窗口大小动态调整发送数据的速率。如果接收方的窗口大小增加,发送方可以加速发送数据。如果窗口大小减小,发送方将减缓发送数据的速率。
- 确认机制:接收方会定期发送确认(ACK)报文,告知发送方已成功接收数据。这也与流量控制密切相关,因为接收方可以通过 ACK 报文中的窗口字段来通知发送方它的当前窗口大小。
4. 拥塞控制:
TCP 还具有拥塞控制机制,通过动态调整发送速率以适应网络状况。当网络出现拥塞时,TCP 会减缓发送速率,以防止进一步加剧拥塞。
- 慢启动(Slow Start):初始阶段,TCP 发送方会以较小的发送窗口开始传输数据。随着每次成功收到确认的数据,发送方逐渐增加发送窗口的大小,实现指数级的增长,这称为慢启动。这有助于在网络刚开始传输时谨慎地逐步增加速率,以避免引发拥塞。
- 拥塞避免(Congestion Avoidance):一旦达到一定的阈值(通常是慢启动阈值),TCP 发送方就会进入拥塞避免阶段。在拥塞避免阶段,发送方以线性增加的方式增加发送窗口的大小,而不再是指数级的增长。这有助于控制发送速率,以避免引起网络拥塞。
- 快速重传(Fast Retransmit):如果发送方连续收到相同的确认,它会认为发生了数据包的丢失,并会快速重传未确认的数据包,而不必等待超时。这有助于更快地恢复由于拥塞引起的数据包丢失。
- 快速恢复(Fast Recovery):在发生快速重传后,TCP 进入快速恢复阶段。在这个阶段,发送方不会回到慢启动阶段,而是将慢启动阈值设置为当前窗口的一半,并将拥塞窗口大小设置为慢启动阈值加上已确认但未被快速重传的数据块的数量。这有助于更快地从拥塞中恢复。
三次握手
TCP(传输控制协议)的三次握手是建立网络连接的过程,确保通信双方能够正确地进行数据传输。
- 第一次握手(SYN):
客户端(Client)向服务器(Server)发送一个带有 SYN(同步)标志位的包,表示客户端希望建立连接。该包同时指定客户端的初始序列号(Client Sequence Number)。 - 第二次握手(SYN + ACK):
服务器收到客户端的 SYN 包后,会回复一个带有 SYN 和 ACK(确认)标志位的包,表示服务器接受了客户端的请求,并希望建立连接。服务器也会指定自己的初始序列号,以及对客户端序列号的确认。 - 第三次握手(ACK):
客户端收到服务器的 SYN+ACK 包后,会发送一个带有 ACK 标志位的包作为确认回复。这个包的序列号会加一,表示客户端已经准备好与服务器进行数据传输。
此时,TCP 连接已经建立起来,通信双方可以开始进行数据传输。
四次挥手
四次挥手是指在 TCP 连接的断开过程中,由客户端先断开,然后由服务器进行最后的断开。
具体的四次挥手步骤如下:
- 客户端发送一个 FIN(终止)报文给服务器,表示客户端不再发送数据。
- 服务器收到 FIN 报文后,发送一个 ACK(确认)报文给客户端,表示收到了客户端的终止请求。
- 服务器发送一个 FIN 报文给客户端,表示服务器也不再发送数据。
- 客户端收到服务器的 FIN 报文后,发送一个 ACK 报文给服务器,确认收到了服务器的终止请求,然后关闭连接。
这样,经过四次挥手,TCP 连接才会完全关闭。因为 TCP 是全双工连接,双方都需要通知对方停止数据传输,所以需要四次挥手来完成断开连接的过程。
TIME_WAIT
time_wait: 当TCP执行主动关闭,并发出最后一个ACK,该链接必须在TIME_WAIT状态下停留的时间为2MSL。
存在的意义:
1、可靠地终止TCP连接:
2、保证让迟来的TCP报文段有足够的时间被识别并丢弃
TIME_WAIT状态存在的必要性。虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,比如丢包或者延迟到达,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文,并保证于此。
TCP KeepAlive
TCP Keep-Alive 是在操作系统和网络协议栈级别实现的,它通过发送特定的探测数据包来维护连接的活跃性。
- 在启用 TCP Keep-Alive 的情况下,操作系统会定期发送一些特定的探测数据包到连接的另一端。这些数据包通常是空的,没有实际的数据内容。
- 如果一端收到了探测数据包,它会回复一个确认(ACK)数据包。如果一段时间内没有收到确认数据包,发送端将认为连接可能已经断开,从而触发连接关闭。
- TCP Keep-Alive 的主要目的是检测连接是否处于空闲状态,即没有实际数据传输。它不仅可以检测到连接断开,还可以在空闲连接超过一定时间时释放连接,从而释放资源。
TCP保活机制
在一个定义的时间段内TCP连接无任何活动时,会启动TCP保活机制,每隔一段时间间隔发送一个探测报文,等待响应
- 对端正常响应,重置保活时间;
- 对端程序崩溃,响应一个RTS报文,将TCP连接重置
- 保活报文不可达,等待达到保活探测次数后关闭连接