Note/扩展知识/TCP概述.md
2024-01-12 16:35:29 +08:00

115 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

面向连接的运输层协议应用程序在使用TCP协议之前必须先建立TCP连接数据传输完毕后必须释放已建立的TCP连接
点对点TCP连接只能有两个端点一对一
可靠通过TCP连接传输的数据无差错、不丢失、不重复、按需到达
全双工通信:连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据
面向字节流:数据通过自己序列发送,发送和接收的字节流顺序必须完全一致
TCP报文段的首部格式
源端口和目的端口各占2个字节
序号占4字节范围是02的32次方减1TCP在传输过字节流的每一个字节都按顺序编号编号循环使用
确认号占4字节期望收到对方下一个报文的数据字节的序号
数据偏移占4位标记报文段的数据起始处距离报文段的起始处的距离
保留占6位
紧急URG当URG为1时表明紧急指针字段有效它告诉接收方此报文段有紧急数据必须优先接收此时该报文不再需需要按照原定的顺序排队直接插入到本报文数据的最前面优先处理比如Control + C命令
确认ACKTCP规定在连接建立后所有传送的报文段都必须把ACK置为1
推送PUSH当输出某些指令后立即需要收到对方响应在这种情况下TCP就可以使用push操作发送方把PUSH置为1并立即创建一个报文发送出去接收方收到PUSH为1的报文段时就立即将缓冲区的报文交付给应用进程而不再等到整个缓冲区都填满后再向上交付
复位RST当RST为1时表明TCP连接中出现严重差错必须释放连接重新建立连接可用于拒绝非法报文或非法连接
同步SYN 在建立连接时用来同步序列号当SYN=1而ACK=0时表明建立连接请求若对方同意建立连接则应该在响应的报文段中使用SYN=1和ACK=1
终止FIN用来释放连接当FIN=1时表示此报文发送方的数据已经发送完毕请求释放TCP连接
窗口占2个字节窗口值作为接收方让发送方设置其发送窗口的依据可靠传输和流量控制都依赖窗口实现后面详细说
检验和计算检验和时要在TCP报文段前面加上伟首部用来检验传输数据是否有错
紧急指针占2个字节在URG=1时才有意义标记紧急报文中的紧急数据的字节数紧急数据结束后就是普通数据。
选项规定每个报文段中的数据字段的最大长度MSS一般在建立连接时双方将自己能够支持的MSS写入选项中以后就按照这个数据传输数据。
数据
报文段首部格式一定要记住,至少要大概理解后,再带着疑问去看下边,否则看下边有可能会一脸懵逼。
TCP可靠传输的实现
TCP的可靠传输通过滑动窗口实现滑动窗口以字节为单位。凡是已发送过的数据在未收到确认之前都必须暂时保留以便在超时重传时使用窗口越大发送方就可以在收到对方确认之前连续发送更多的数据获得更高的传输速率。滑动窗口可以分为四部分
已发送并搜到确认
已发送未收到确认
允许发送但尚未发送
不允许发送
窗口依赖三个指针P1P2和P3小于1的是已经发送并收到确认的字节1和2之间的是已发送但未收到确认的字节2和3之间是允许发送但尚未发送但字节大于3为不允许发送的字节1和3之间的字节数即为接收方规定的滑动窗口大小流量控制。接收方B只能对按需收到的最高序号给出确认当指针2和指针3重合时发送方必须停止发送数据等待接收方的确认为了保证可靠传输A必须等到B的确认才能继续发送数据为了保证发送方不会一直等待到达依赖超时计时器动态计算超时重传时间规定的时间后就重传这部分数据直到收到接收方B的确认。
超时重传时间的选择采用一个自适应算法它会记录一个报文段发出的时间以及收到响应的确认的时间作为往返时间RTT计算为加权平均往返时间RTTsRTTd为RTT的偏差的加权平均值通过这几个值计算得到超时重传时间RTO。计算的规则是报文段每重传一次就把超时重传时间RTO增大一些当不再发生报文段的重传时根据下面公式计算RTO。
RTO = RTTs + 4 * RTTd
TCP的流量控制
流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收,利用滑动窗口实现。发送方的发送窗口不能超过接收方给出的接收窗口大小。当接收方接收能力下降时,会缩小自己的接收窗口,发送方通过报文头部的串口字段告知发送方。
还以上面发送方为A接收方为B作为例子当B的接收缓存满之后将自己的接收窗口置为0通过报文的窗口字段告知发送方A过了一段时间后B的应用程序将接收缓冲区的数据处理了一部分接收窗口又有了空间假设为300此时B向A发送报文告知对方接收窗口为300如果该报文在传输中丢失了A不会一直傻傻地等待在A收到B的0窗口报文后就会开启该连接设定的持续计时器当超过计时器规定的时间后A会向B发送一个0窗口探测报文段B在收到这个探测报文后会重新发送300的窗口报文段。此时有人会问B的接收窗口大小为0了怎么还能接收0窗口探测报文段这里需要记住TCP规定即使接收窗口为0也必须可以接收0窗口探测报文段、ACK=1的报文段和URG=1的报文段
TCP的拥塞控制
TCP进行拥塞控制的算法有4个慢开始、拥塞避免、快重传、快恢复。
慢开始将拥塞窗口大小设置为1个发送方的最大报文段MSS上面说过哈报文首部的选项字段每经过一个传输轮次拥塞窗口就加倍直到达到慢开始门限后开启拥塞避免。
拥塞避免当达到慢开始门限后每收到一次接收方的确认ACK就将拥塞窗口大小+1直到网络出现超时后将慢开始门限调整为原来的一半执行慢开始算法。
快重传快重传算法可以让发送方尽早知道发生了个别报文段的丢失。由于接收方必须对发送方每个报文回复确认ACK假设发送方以此发送M1、M2、M3、M4、M5、M6报文接收方收到1和2后都及时给出了确认由于字节都是连续都假设接收方没有收到3但收到了4此时接收方会在收到4、5、6报文后都发送对2的确认当发送方连续收到3个重复确认时就知道接收方没有收到报文段3此时会立即重传报文3。
快恢复:当发生快重传时,发送方知道现在只是丢失了个别字段,所以不启动慢开始,而是执行快恢复算法,将拥塞窗口调整为慢开始门限的一半,执行拥塞避免算法。
下面通过一道题来理解:
问题设tcp的拥塞窗口的慢开始门限值初始为8,当拥塞窗口上升到12时网络发生超时那么第13次传输时的拥塞窗口大小为多少
解答:
慢开始0->1->2->4->8传输4次
到达慢开始门限8进入拥塞避免 8->9->10->11->12传输4次
增长到12发生超时慢开始门限调整到6慢启动0->1->2->4->6传输4次
6已经达到慢开始门限进入拥塞避免6->7第十三次传输
所以最终答案为7
TCP连接的三次握手
最开始的 Client 和 Server 都是处于 Closed由于服务器端不知道要跟谁建立连接所以其只能被动打开然后监听端口此时 Server 处于 Listen 状态;
而 Client 会主动打开,然后构建好 TCB 「SYN= 1seq = x」发送给服务器端此时 Client 会将状态置为 SYN_SEND 「同步已发送」;
服务器端收到客户端发来的同步请求后,会将状态置为 SYN_RECV「同步已接收」同时会构建好 TCB「SYN = 1seq = yACK = 1ack = x + 1」发送给客户端
客户端接收到了服务器端发来的传输控制块之后,会将自己的状态改为 ESTABLISHED「建立连接」然后发送确认报文ACK= 1seq = x + 1ack = y + 1
服务器端在收到了客户端发来的报文之后,也将状态置为 ESTABLISHED「建立连接」至此三次握手结束当然在这里可以带 tcp 报文段信息过来了,因为此时客户端已经可以保证是可靠的传输了,所以在这一端可以发送报文段了。
题目1为什么A最后还要再发送一次确认
解答为了防止因为网络原因迟来的A连接请求到达BB单方面建立连接浪费B的资源。假如A发送了连接请求但未收到确认于是A又重传了一次连接请求第二个连接请求正常连接并传输数据。此时A的第一个连接没有丢失只是延迟了又到达了BB误以为是A又发送的一次连接假如此时B向A发出确认后A会丢弃B的确认假如没有A最后一次的确认B就会在发送确认后建立连接白白浪费B的资源。
题目2为何不直接在第一次握手就带上报文段消息非要第三次才可以带
解答:因为 TCP 是要保证数据的不丢失且可靠如果在第一次就带上报文段消息此次建立连接很有可能就会失败那么就不能保证数据的不丢失了在不可靠的机制上进行这种操作换来的代价太大每次发送报文段的资源也会增大得不偿失而第三次握手的时候客户端已经知道服务器端准备好了所以只要告诉服务器端自己准备好了就ok了所以此时带上报文段信息没有任何问题。
TCP断开的四次挥手
最开始客户端和服务器端都是 ESTABLISHED 的状态,然后客户端会主动关闭,而服务器端则是被动关闭。(主动关闭的我们认为是客户端)
客户端发送一个 FIN 报文段seq = 结束的报文段序号 + 1「假设为 u」告诉服务器端客户端需要关闭了此时将客户端的状态变为 FIN-WAIT-1等待服务器端的反馈
服务器在接收到了客户端发来的 FIN 包之后,会发一条 ack报文反馈给客户端其中报文中包括 ACK = 1seq = vack = u+1告诉客户端收到了客户端要关闭的消息了同时服务器端会通知应用进程需要关闭连接了并将自己的状态置为 CLOSE-WAIT
由于服务器端可能还有一些数据没处理完,所以需要一段时间的等待,当处理完了之后,会再发一条报文,其中 FIN = 1ACK = 1seq = wack = u+1告知客户端服务器端现在可以关闭了并将服务器端的状态由 CLOSE-WAIT 变为 LAST-ACK
客户端在收到了服务器端发来的消息之后会发一条ack报文「ACK = 1seq = u+1ack = w+1」回去告知服务器端客户端已经知道了你准备好关闭了此时会将客户端的状态由 FIN-WAIT-2 置为 TIME-WAIT在两个最长报文段传输时间过后会自动将客户端的状态由 TIME-WAIT 置为 CLOSED。
服务器端收到消息之后,就将状态由 LAST-ACK 置为了 CLOSED自此四次挥手全部结束。
题目1在B向A发送FIN断开连接的报文后A发送确认后做了什么是马上断开吗为什么要等待2个时间周期
解答图里很明了并不是马上断开而是等待了2个时间周期。原因有2个
为了保证A发送的最后一个ACK报文能到达B在这两个时间周期内如果B收不到A的确认会超时重传这个FIN+ACK的报文A就会收到重传的断开确认报文此时A会重传确认重新启动2MSL计时器。假如A不等待2MSL就不会收到B的重传报文也就不会再发送一次确认报文B就无法按照正常步骤进入CLOSE状态。
A在发送完最后一个ACK报文后经过2MSL就可以使本次连接内产生的所有报文都从网络中消失在下一个新连接中就不会出现旧的连接报文段。
题目2主动关闭方和被动关闭方谁先进入CLOSE状态
解答看图也可以发现是被动关闭方先进入CLOSE状态原因上一题的答案就可以解释明白。
题目3:为何不能三次挥手呢?
解答:首先如果去掉最后一次挥手,那么服务器端就不知道自己要关闭的报文有没有传输成功,可能半路上就失败了,但是此时客户端不知道,导致客户端一直在等待服务器关闭,但是此时服务器端直接就关闭了。