目录
[2.1 TCP可靠性的保障](#2.1 TCP可靠性的保障)
4.1问题(1):为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?
4.2问题(2):为什么连接建立的时候是三次握手,可以改成两次握手吗?
4.3问题(3):为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间?
4.4问题(4):如果已经建立了连接,但是Client端突然出现故障了怎么办?
1、TCP概念及协议头部格式
TCP(传输控制协议)是一种面向连接(连接导向)的、可靠的、 有序、无丢失和无重复,基于IP的传输层协议
1.1TCP特点
-
TCP是一种面向连接的传输协议,只能适应于单播的数据通信
-
每一条TCP连接有且只能存在两个端点,形成一种端到端的连接形式。
-
可靠、有序、无丢失和无重复
-
TCP是提供全双工通讯。
发送缓存:已经发送但未收到确认的数据 想要发送的应用层数据
接收缓存:按需到达但还未被应用程序提取的数据 乱序到达的数据
- TCP是面向字节流的------(套接字:IP:Port 即源IP、源端口、目IP、目端口----->TCP会话的四元组信息)
1.2TCP协议协议头部格式
TCP首部如果不计选项和填充字段,它通常是20个字节。
1.3字段进行介绍
1.3.1源端口和目的端口
TCP协议是基于IP协议的基础上传输的,TCP报文中的源端口号+源IP,与TCP报文中的目的端口号+目的IP一起,组合起来唯一性的确定一条TCP连接。即表示数据从哪个进程发送,发送到哪个进程去
1.3.2序号(seq)
TCP传输过程中,在发送端出的字节流中,传输报文中的数据部分的每一个字节都有它的编号。序号占32位,发起方发送数据时,都需要标记序号。
当SYN = 1时,当前为连接建立阶段,此时的序号为初始序号ISN((Initial Sequence
Number),通过算法来随机生成序号;
在数据传输过程中,TCP协议通过序号对上层提供有序的数据流。发送端可以用序号来跟踪 发送的数据量;接收端可以用序号识别出重复接收到的TCP包 ,从而丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进行排序。
1.3.3确认序号(ack)
占4字节,是期望收到对方下次发送的数据的第一个字节的序号,也就是期望收到的下一个报文段的首部中的序号;确认序号应该是上次已成功收到数据字节序号+1。
只有ACK标志为1时,确认序号才有效。
确认序列号表明是接收方期望收到发送方发送的下一个字节的序号;且表示之前的所有数据均已接收------------累积确认
1.3.4数据偏移
占4比特,表示数据开始的地方离TCP段的起始处有多远。实际上就是TCP段首部的长度。由于首部长度不固定,因此数据偏移字段是必要的。
数据偏移以32位为长度单位,也就是4个字节,因此TCP首部的最大长度是60个字节。即偏移最大为15个长度单位=1532位=154字节。
1.3.5标志位
- ACK确认位:当ACK=1时,确认序列号有意义。在连接建立后所有传输的报文段都必须将该标记位置为1。
- SYN同步位:代表连接请求。
- FIN终止位:表明此报文段发送方数据已发送完毕,要求释放连接。
- RST复位:当TCP连接出现严重错误时,必须释放连接,然后重新建立传输连接。
- URG紧急位:当URG=1时,表明此报文段中存在紧急数据,是高优先级数据,应尽快传输给应用层程序处理,不再缓存在排队。配合紧急指针使用。
- PSH推送位:当PSH=1时,接收方应尽快交付数据给应用层程序,不再等待缓存填满再向上交付。
1.3.6窗口
长度为16位,共2个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。
所谓滑动窗口,可以理解成接收端所能提供的缓冲区大小。TCP利用一个滑动的窗口来告诉发送端对它所发送的数据能提供多大的缓 冲区。
1.3.7校验和
长度为16位,共2个字节。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,接收端用于对收到的数据包进行验证。
1.3.8紧急指针
只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。(标识哪部分数据是紧急数据,优先处理这部数据)
2、TCP的可靠性
2.1 TCP可靠性的保障
(1)应用数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最大数据包长度)选项来控制的,通常这种机制也被称为一种协商机制,MSS规定了TCP传往另一端的最大数据块的长度。值得注意的是,MSS只能出现在SYN报文段中,若一方不接收来自另一方的MSS值,则MSS就定为536字节。一般来讲,MSS值还是越大越好,这样可以提高网络的利用率。
(2)重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报文重传。
(3)对首部和数据进行校验。
(4)接收端对收到的数据进行排序,然后交给应用层。
(5)接收端丢弃重复的数据。
(6)TCP还提供流量控制,主要是通过滑动窗口来实现流量控制。
至此TCP协议的数据帧格式介绍完了。接下来开始为大家重点介绍:TCP传输层的三次握手建立连接,四次挥手释放连接。
2.2排序机制
当进行一条数据发送的时候,首先,TCP会将这条数据拆分成不同的片段,然后把片段进行一个排序。然后把排序号的片段顺序进行发送。这样,保证了传输的有序性。
- MTU---最大传输单元
- MSS---最大段长度----TCP分段--->该参数是需要在TCP建立握手过程中通过前两次SYN报文段来进行协商确定。
如果在本地进行了分段操作,则不需要进行分片操作
2.3确认应答机制
向对方发送一个数据报,对方要返回一个确认应答的数据报
实现的方式:序号和确认序号保证了响应应答针对的是哪一条消息的应答
实际上,TCP的序号并不是一条一条来编号的,因为TCP是面向字节流的,所以TCP的序号也是按照字节来编号的 假设一条数据是1000个字节,那么假设他从1开始编号,此时第一个字节序号就是1,第二个字节序号就是2......以此类推,最后一个字节序号就是1000。
但是由于这1000个字节都属于同一个TCP报文,所以TCP报头里就只记录当前的第一个字节序号,当A发送的第一条数据到达B之后,B会返回一个应答报文,该应答报文的序号是1001,表示前1000个字节的数据我已经收到了,现在A现在要发送第1001个字节的数据了(B向A索要1001的数据);
2.4重传机制
TCP要保证所有的数据包都可以到达,所以,必需要有重传机制。
注意: 接收端给发送端的Ack确认只会确认最后一个连续的包,比如,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到)此时的TCP会怎么办?
我们要知道,因为正如前面所说的,SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了。
2.4.1超时重传机制
一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 回 4------意味着3和4都收到了。(2.4的问题)
但是,这种方式会有比较严重的问题,那就是因为要死等3,所以会导致4和5即便已经收到了,而发送方也完全不知道发生了什么事,因为没有收到Ack,所以,发送方可能会悲观地认为也丢了,所以有可能也会导致4和5的重传。
对此有两种选择:
① 一种是仅重传timeout的包。也就是第3份数据。
② 另一种是重传timeout后所有的数据,也就是第3,4,5这三份数据。
这两种方式有好也有不好。第一种会节省带宽,但是慢,第二种会快一点,但是会浪费带宽,也可能会有无用功。但总体来说都不好。因为都在等timeout,timeout可能会很长。
总结
在确认应答机制中,只是做出了数据传输顺利的情况,但是如果数据传输不顺利呢?A发送完后却数据迟迟收不到应答报文即 "丢包" 情况;丢包主要有两种:
1、A发送的数据丢了 ;
2、B返回的应答报文(ack)丢了;
当出现丢包情况时,TCP就会启动超时重传机制,即重新发送一遍相同的数据;
在这里,TCP引入了一个时间阈值,当发送方发送数据时,就会开始计时,如果到达时间阈值后也没有收到接收方返回的ack,此时不管是ack还在路上还是丢包了,都统一认为是丢包,发送方会重新发送数据;超时重传机制:超过一定时间,还未响应,就重新发送数据;
但是这个时间阈值具体时间多少,就要根据系统来决定,没有一个绝对值;
2.4.2快速重传机制
RTT---往返时间
RTO---超时重传时间;略大于RTT时间--->动态变化的数值。加倍的形式进行变化。
在快速重传机制中,并不是因为RTO时间到达从而触发重传机制,该重传机制是根据对端的反馈信息进行重传,当连续3三收到相同的ACK报文时,发送端会重传数据。这3个连续的ACK报文被称为冗余ACK
2.5选择确认机制
若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送缺少的数据而不重传已经正确到达接收方的数据?
答案是可以的。选择确认就是-种可行的处理方法。
在TCP首部的选项字段提供了SACK的选项,它可以将已收到的数据的信息(边界信息)发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据
注意:选择确认机制也是需要进行协商的
2.6TCP流量控制(滑动窗口协议)
TCP流量控制主要是针对接收端的处理速度不如发送端发送速度快的问题,消除发送方使接收方缓存溢出的可能性。
TCP流量控制主要使用滑动窗口协议,滑动窗口是接受数据端使用的窗口大小,用来告诉发送端接收端的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量控制的目的。这个窗口大小就是我们一次传输几个数据。对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才允许被发送;同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才允许接收。这样通过调整发送方窗口和接收方窗口的大小可以实现流量控制。
窗口:指定的是无需等待确认应答,而可以继续发送数据包的最大值。
窗口大小体现在缓存区的大小
TCP要求发送方依据接收窗口rwnd来控制数据的发送量。rwnd等于接收方接收缓存大小减去已存数据量大小。即rwnd变量是可变的
- 接收端将自己剩余缓冲区大小存入TCP头部中的"16位窗口大小"字段 ,通过ACK通知发送端
- 窗口大小越大,说明网络吞吐量越高
- 发送端根据接收到这个窗口的大小,控制自己的发送速度
- 如果接收缓冲区满了,就会将窗口设置为0,这时,发送端不在发送数据,而是定期的发送一个窗口探测报文(只是为了知道窗口的大小),让接收端将窗口大小告诉发送端
2.7拥塞控制
流量控制是通过接收方来控制流量的一种方式;而拥塞控制则是通过发送方来控制流量的一种方式。TCP发送方可能因为IP网络的拥塞而被遏制,TCP拥塞控制就是为了解决这个问题(注意和TCP流量控制的区别)。
TCP拥塞控制的几种方法:慢启动,拥塞避免,快重传和快恢复。
这里先理解一个概念: 拥塞窗口
拥塞窗口:发送方维持一个叫做拥塞窗口 cwnd的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化。
发送方的让自己的发送窗口=min(cwnd,接受端接收窗口大小)。说明: 发送方取拥塞窗口与滑动窗口的最小值作为发送的上限。
发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
3、TCP面向连接
3.1TCP连接的建立
TCP连接建立需要解决的问题:
1、要使双方均知晓对方的套接字信息。
2、允许双方进行参数协商(MSS、窗口值、是否使用选择确认机制)
3、给各设备进行资源分配
3.2.1三次握手图解
TCP连接的建立时,双方需要经过三次握手,具体过程如下:
(1)第一次握手:Client进入SYN_SENT状态,发送一个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。
(2)第二次握手:Server端在收到SYN帧之后,会进入SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。
(3)第三次握手:Client在收到Server的第二次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。
(4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。
3.2TCP连接释放
1、对双方各自资源的释放过程
2、任何一方都可以在数据传输结束后发送连接释放通知
四次挥手具体过程,具体如下:
(1)第一次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。
(2)第二次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文,报文的Acknowledgment Number(确认号)值为断开请求报文的Sequence Number(序列号)加1,该ACK确认报文的含义是:"我同意你的连接断开请求"。之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。
(4)第四次挥手:主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文,然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。
被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。
3.2.1四次挥手图解
4、三次握手、四次挥手的常见面试题
4.1问题(1):为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?
关闭连接时,被动断开方在收到对方的FIN结束请求报文时,很可能业务数据没有发送完成,并不能立即关闭连接,被动方只能先回复一个ACK响应报文,告诉主动断开方:"你发的FIN报文我收到了,只有等到我所有的业务报文都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报文的,你先等着"。所以,被动断开方的确认报文,需要拆开成为两步,故总体就需要四步挥手。
而在建立连接场景中,Server端的应答可以稍微简单一些。当Server端收到Client端的SYN连接请求报文后,其中ACK报文表示对请求报文的应答,SYN报文用来表示服务端的连接也已经同步开启了,而ACK报文和SYN报文之间,不会有其他报文需要发送,故而可以合二为一,可以直接发送一个SYN+ACK报文。所以,在建立连接时,只需要三次握手即可。
4.2问题(2):为什么连接建立的时候是三次握手,可以改成两次握手吗?
关闭连接时,被动断开方在收到对方的FIN结束请求报文时,很可能业务数据没有发送完成,并不能立即关闭连接,被动方只能先回复一个ACK响应报文,告诉主动断开方:"你发的FIN报文我收到了,只有等到我所有的业务报文都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报文的,你先等着"。所以,被动断开方的确认报文,需要拆开成为两步,故总体就需要四步挥手。
如果把三次握手改成两次握手,可能发生死锁。两次握手的话,缺失了Client的二次确认ACK帧,假想的TCP建立的连接时二次挥手,可以如下图所示:
在假想的TCP建立的连接时二次握手过程中,Client发送Server发送一个SYN请求帧,Server收到后发送了确认应答SYN+ACK帧。按照两次握手的协定,Server认为连接已经成功地建立了,可以开始发送数据帧。这个过程中,如果确认应答SYN+ACK帧在传输中被丢失,Client没有收到,Client将不知道Server是否已准备好,也不知道Server的SN序列号,Client认为连接还未建立成功,将忽略Server发来的任何数据分组,会一直等待Server的SYN+ACK确认应答帧。而Server在发出的数据帧后,一直没有收到对应的ACK确认后就会产生超时,重复发送同样的数据帧。这样就形成了死锁。
4.3问题(3):为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间?
**原因之一:**主动断开方等待2MSL的时间,是为了确保两端都能最终关闭。假设网络是不可靠的,被动断开方发送FIN+ACK报文后,其主动方的ACK响应报文有可能丢失,这时候的被动断开方处于LAST-ACK状态的,由于收不到ACK确认被动方一直不能正常的进入CLOSED状态。在这种场景下,被动断开方会超时重传FIN+ACK断开响应报文,如果主动断开方在2MSL时间内,收到这个重传的FIN+ACK报文,会重传一次ACK报文,后再一次重新启动2MSL计时等待,这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。只有这样,双方都能够确保关闭。反过来说,如果主动断开方在发送完ACK响应报文后,不是进入TIME_WAIT状态去等待2MSL时间,而是立即释放连接,则将无法收到被动方重传的FIN+ACK报文,所以不会再发送一次ACK确认报文,此时处于LAST-ACK状态的被动断开方,无法正常进入到CLOSED状态。
**原因之二:**防止"旧连接的已失效的数据报文"出现在新连接中。主动断开方在发送完最后一个ACK报文后,再经过2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。
4.4问题(4):如果已经建立了连接,但是Client端突然出现故障了怎么办?
、TCP还设有一个保活计时器,Client端如果出现故障,Server端不能一直等下去,这样会浪费系统资源。每收到一次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2小时,若2小时还没有收到Client端的任何数据帧,Server端就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多小时的间隔太长,可以自行调整TCP连接的保活参数。