确认应答机制
TCP将每个字节的书序都进行编号,即为序列号,每一个ACK都带有对应的确认序列号,告诉发送者,我已经收到了那些数据;下次你应该从哪里开始发。
超时重传机制
在上述通信过程中,主机A如果没有接收到主机B的应答会怎么办?可能是主机A在发送的途中丢包了,也有可能主机B回主机A的途中丢包了。
TCP会通过序列号做到去重的效果。?
超时的时间是逐步增加的,一般都是第第一次发送的整数倍。
超时重传的次数也是有上限的,达到上限后TCP会关闭本次连接、TCP会通知进程出现异常(通过抛出异常)、最后发送一个reset segment(重置报文)。
连接管理机制
三次握手
第一次握手:建立连接时,客户端发送SYN包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
SYN:同步序列编号
四次挥手
- 第一次挥手:A->B,A向B发出释放连接请求的报文,其中FIN(终止位) = 1,seq(序列号)=u;在A发送完之后,A的TCP客户端进入FIN-WAIT-1(终止等待1)状态。此时A还是可以进行收数据的
- 第二次挥手:B->A:B在收到A的连接释放请求后,随即向A发送确认报文。其中ACK=1,seq=v,ack(确认号) = u +1;在B发送完毕后,B的服务器端进入CLOSE_WAIT(关闭等待)状态。此时A收到这个确认后就进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放的请求。此时B还是可以发数据的。
- (如果 B 直接跑路,则 A?永远处与这个状态。TCP 协议里面并没有对这个状态的处理,但 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。)
- 第三次挥手:B->A:当B已经没有要发送的数据时,B就会给A发送一个释放连接报文,其中FIN=1,ACK=1,seq=w,ack=u+1,在B发送完之后,B进入LAST-ACK(最后确认)状态。
- 第四次挥手:A->B;当A收到B的释放连接请求时,必须对此发出确认,其中ACK=1,seq=u+1,ack=w+1;A在发送完毕后,进入到TIME-WAIT?(时间等待)状态。B在收到A的确认之后,进入到CLOSED(关闭)状态。在经过时间等待计时器设置的时间之后,A才会进入CLOSED状态。
注意:客户端在发送完最后一次确认之后,还要等待2MSL的时间。主要有两个原因,一个是为了让B能够按照正常步骤进入CLOSED状态,二是为了防止已经失效的请求连接报文出现在下次连接中。
TCP状态转换汇总图
?流量控制
? 接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。 因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制。而滑动窗口机制可以用来实现接收端处理能力的判断。
滑动窗口
窗口的概念
发送方的发送缓存内的数据都可以被分为4类:1. 已发送,已收到ACK2. 已发送,未收到ACK3. 未发送,但允许发送4. 未发送,但不允许发送
其中类型2和3都属于发送窗口。
接收方的缓存数据分为3类:1. 已接收2. 未接收但准备接收3. 未接收而且不准备接收
其中类型2属于接收窗口。
窗口大小代表了设备一次能从对端处理多少数据,之后再传给应用层。缓存传给应用层的数据不能是乱序的,窗口机制保证了这一点。现实中,应用层可能无法立刻从缓存中读取数据。
滑动机制
- 发送窗口只有收到发送窗口内字节的ACK确认,才会移动发送窗口的左边界。
- 接收窗口只有在前面所有的段都确认的情况下才会移动左边界。当在前面还有字节未接收但收到后面字节的情况下,窗口不会移动,并不对后续字节确认。以此确保对端会对这些数据重传。
- 遵循快速重传、累计确认、选择确认等规则。
- 发送方发的window size = 8192;就是接收端最多发送8192字节,这个8192一般就是发送方接收缓存的大小。
?
拥塞控制
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大 量的数据,仍然可能引发问题。所以TCP引入了慢启动机制。
慢启动机制
- 先发送少量数据,试探当前网络的拥堵状态,再决定按照多大的速度传输数据。
- 此处引入一个概念程为拥塞窗口
- 发送开始的时候,定义拥塞窗口大小为1;
- 每次收到一个ACK应答,拥塞窗口加1;
- 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为 实际发送的窗口;
但是拥塞窗口的增长指数是指数级别,所以后期增长速度会趋于线性。
- 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。
- 此处引入一个叫做慢启动的阈值
- 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长
- ?当TCP开始启动的时候,慢启动阈值等于窗口最大值;
- 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;
少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞;
当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降; 拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的 折中方案。