目录
一.TCP概念及其特性
二.TCP原理
1.确认应答机制
2.超时重传机制
3.连接管理机制
三次握手的过程
四次挥手断开连接过程
四次挥手断开连接变形情况:
为什么连接阶段时三次握手,断开连接是四次挥手?
服务器上面出现大量的CLOSE_WAIT状态的TCP连接,请问这种现象是否合理?
既然主动关闭端挥手的工作已经完成了,为什么还有TIME_WAIT状态而不直接进入CLOSED状态?
为什么是TIME_WAIT的时间是2MSL?
4.流量控制
5.滑动窗口机制
那如果在传输数据时出现了数据丢失,如何进行重传?
6.拥塞控制
7.延迟应答机制
8.捎带应答机制
传输控制协议:TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。 用户数据报协议(UDP)是同一层内另一个重要的传输协议。
特性:可靠性,有连接,面向字节流
什么是可靠性:
1.TCP会尽自己所能,尽量将数据发送给对方
2.TCP汇集那个数据没有发给对方的情况下,给应用层一个错误通知
3.TCP可以保证接收方(应用层)严格按照发送时的顺序接收
4.TCP保证数据不会无意间的损坏
5.TCP尽可能的维护网络质量
发送方发送数据,如果收到了接收方的应答(确认),就代表了接收方收到了数据;
如果超过一定时间没有收到接收方的应答(确认),可以推测接收方未能收到数据。
*如果发送方同时发送了多条数据,如何确认接收方的确认信息是指的那一条数据?
答:对数据进行编号,接收方发出确认信息的时候也带上编号,就可以明确接受的数据。
对数据进行编号
1.发送的数据编号——序列号 SN(Sequence Number)
2.确认时的数据编号——确认序列号 ASN(Acknowledge Sequence Number)
3.初始序列号ISN(initial sequence number):在 TCP 建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号。
* 如果超过一定时间没有收到接收方的确认,该如何操作?
答:进行重发。进行重发需要触发的条件:超过一定时间没有收到接收方的确认
先思考:如果没有收到对方发送的应答,可能的情况有哪些
1.对方没有收到你的数据所以没有应答;又分为以下两种情况
(1):数据还在路上,暂时还没有到达对方
(2):数据在路上丢失了,所以不会有应答
2.对方收到数据了发出来应答,但是应答我没有收到;又分为以下两种情况
(1):应带还在路上,我们暂时还没有收到
(2):应答在路上丢失,我们不会收到应答
如果是上面1(1),2(1)这两种情况,我们可以选择合适的时间差进行重发;
如果是1(2),2(2)这两种情况,我们是否能选择重发,还是需要进一步的辨别?
答案是重发,看看下面的分析:
主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B;
如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发;
主机B会收到很多重复数据。那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。这时候我们可以利用前面发送数据时的序列号,就可以很容易做到去重的效果。
那该如何选择再次发送数据的时间?
这个时间的长短,随着网络环境的不同,是有差异的。如果超时时间设的太长,会影响整体的重传效率;如果超时时间设的太短,有可能会频繁发送重复的包;
一般来说会略大于往返时间RRT(Round Trip Time),RTT需要进行多次测量计算得来
新RTTS=(1−α)×旧RTTS+α×RTT
a的取值为:0≤α≤1,RFC推荐的αα值为1/8,即0.125。通过上面的公式得出的加权平均往返时间RTTs就比直接测量得出的RTT更加平滑。TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。
如果是网络(物理层)出现问题,会一直的重发下去吗?
不会,TCP会尝试重发几次(重发的次数不同版本的操作系统会有不同),如果还是收不到应答,就会停止发送。然后需要通知应用层,发送失败。
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接。
TCP的主动连接方一般为客户端
TCP的被动连接方一般为服务器
来看一看客户端和服务器在三次握手中的状态变化
客户端:
CLOSED -> SYN_SENT : 客户端调用connect,发送同步报文段;
SYN_SENT -> ESTABLISHED : connect调用成功,则进入ESTABLISHED状态,开始读写数
据;
服务器:
CLOSED -> LISTEN: 服务器端调用listen后进入LISTEN状态,等待客户端连接;
LISTEN -> SYN_RCVD: 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列
中,并向客户端发送SYN确认报文。
SYN_RCVD -> ESTABLISHED: 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状
态,可以进行读写数据了。
三次握手阶段是否可以携带payload?
1.第一次握手,主动连接方发送syn,不可以携带数据
2.第二次握手,被动连接方发送syn+ack,不可以携带数据
3.第三次握手,主动连接方发送ack,可以携带数据
原因:因为在第一次和第二次握手阶段时,不能确认连接一定建立成功,如果携带了数据,则会提升发送成本,还会接受失败,所以TCP协议设计时禁止了携带数据;第三次握手连接已经建立成功,所以第三次握手可以发送数据。
TCP三次握手阶段SN与ASN的变换
主动连接方初始化序列号为:ISN : a ;
被动连接方初始化序列号为:ISN : b ;
第一次握手阶段 :syn len = 0; SN = a ;ASN = 0;
第二次握手阶段 :syn + ack len = 0; SN = b ; ASN = a+1;
第三次握手阶段 : ack len = ? ; SN = a+1 ; ASN = b+1;
由于第一二次握手阶段不能携带数据,所以len = 0;第三次握手阶段中 "?"表示携带数据的长度;
四次挥手断开连接的状态变化
客户端:
ESTABLISHED -> FIN_WAIT_1: 客户端主动调用close时,向服务器发送结束报文段,同时进
入FIN_WAIT_1;
FIN_WAIT_1 -> FIN_WAIT_2: 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,
开始等待服务器的结束报文段;
FIN_WAIT_2 -> TIME_WAIT: 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发
出LAST_ACK;
TIME_WAIT -> CLOSED: 客户端要等待一个2MSL(Max Segment Life,报文最大生存时
间)的时间,才会进入CLOSED状态。
服务器:
ESTABLISHED -> CLOSE_WAIT: 当客户端主动关闭连接(调用close),服务器会收到结束
报文段,服务器返回确认报文段并进入CLOSE_WAIT;
CLOSE_WAIT -> LAST_ACK:进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前
的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入
LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
LAST_ACK -> CLOSED: 服务器收到了对FIN的ACK,彻底关闭连接。
三次挥手
双方同时挥手
当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。所以断开连接是四次挥手
*CLOSE_WAIT一定出现在单方面分手的被动关闭端
无法确定是否合理;如果我们的程序设计时,会出现比较长时间的单方面关闭的情况时,会出现大量CLOSE_WAIT是合理的现象,但如果我们没有这种设计,则不合理,可能出现的原因是忘记调用socket.close()方法。
TIME_WAIT 状态发生在主动关闭端,出现在基本挥手已经结束的情况;
(1) TIME_WAIT存在的理由之一是尽可能护送最后的ACK达到对端。
因为不能确保发出去的ACK对方一定会收到,所以还需要等待一段时间,如果对方没有收到ACK,主动关闭端需要重发FIN,如果主动关闭端处于CLOSED的状态,对方就无法收到最后的ACK。
(2)为了使就得数据包在网络中因过期而失效
假设没有time_wait状态时,A刚刚与B断开连接,C又以和A相同的ip和port和B建立起连接,TCP协议栈无法区分A和C是不同的连接, 这时,A连接发送的数据到达B之后会被B的TCP传输层当做当下正常的连接发来的数据进行处理。
MSL,Maximum Segment Lifetime,最大报文段生存时间。即任何TCP报文在网络中存在的最大时长,如果超过这个时间,这个TCP报文就会被丢弃。
因为主动关闭端不知道对方是否能收到ACK应答数据包,如果没有收到ACK,会进行重传FIN,考虑最坏的一种情况:第四次挥手的ACK包的最大生存时长(MSL)+服务端重传的FIN包的最大生存时长(MSL)=2MSL。
现象:服务器上发现了大量的TIME_WAIT状态的TCP连接,是否合理?不合理,给出修复意见。
理论上是合理的,但是如果影响了导致了新连接创建失败,就不太合理了
一般来说客户端背负的连接比较少,服务器背负的连接远远多于客户端。由于维护连接是需要成本的,所以不建议服务器去主动关闭连接。
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做流量控制(Flow
Control);
接受窗口:接收缓存区中剩余空间的大小
作为发送方,如何实时的知道接收方的接受能力?
1.发送Segment Header,将自己的的接收能力(接收窗口的大小)填写到窗口字段中,发送给对方;
2.TCP即使没有数据发送时 ,也会定期发送Segment Header ,传递最新的接受能力;
先了解一下发送缓冲区的逻辑部分
连接建立阶段,通过三次握手,就可以知道对方的接收窗口
应用层写入了需要发送的数据
紫色部分:发送为应答部分
蓝色部分:应用层写入的还未发送的数据
了解这些知识后,再来看看滑动窗口机制时如何实现的
应用层写入数据窗口增加,收到接收方的应答后减小窗口,实现滑动窗口机制。
分为两种情况:
1.接收方的应带丢失
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认;
2.发送数据的数据包丢失(快重传机制)
当某一段报文段丢失之后,发送端会一直收到 1001 这样的ACK;
如果发送端主机连续三次收到了同样一个 "1001" 这样的应答,就会将对应的数据 1001 -
2000 重新发送;
这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端
其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中;
这种机制被称为 "高速重发控制"(也叫 "快重传")。
网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,如何发送数据;
拥塞控制:根据网络目前的承载能力来控制数据的发送量;
拥塞窗口 (cwnd):是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化的。
横坐标:时间
纵坐标:当前计算出的拥塞窗口(cwnd)
慢开始:拥塞窗口的初始值非常小
指数增长->线性增长中间阈值(ssthresh) cwnd = cwnd^c ;上图中(c 为1)
线性增长->cwnd = cwnd + c(常数)
当遇到了网络拥塞,慢启动阈值会变成原来的一半,同时拥塞窗口(cwnd)置回1;
假设接收端缓冲区为1M。一次收到了500K的数据;如果立刻应答,返回的窗口就是500K;但实
际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了;在这种情况下,接
收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;如果接收端稍微等一会
再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1M;
数量限制:每隔N个包就应答一次;
时间限制:超过最大延迟时间就应答一次;
具体的数量和超时时间,依操作系统不同也有差异;一般N取2,超时时间取200ms;
可以减少应答次数,将应答和数据一起发送,提高效率。