@Catyee
2021-08-04T21:57:41.000000Z
字数 6909
阅读 511
面试
1、tcp保证可靠的机制有哪些?
2、tcp提高效率的机制有哪些?
3、tcp中有几种计时器?
4、为什么要三次握手而不是两次?
5、第三次握手时客户端的ack包丢失会怎么办?
6、第三次握手时数据包先于ack包到了怎么半?
7、为什么要四次挥手?
8、客户端关闭之后为什么要等待2MSL的时间?
9、tcp连接建立之后,断开服务端,然后快速启动服务端,会发生什么情况?
10、滑动窗口中包丢失怎么办?
11、超时重传的超时时间如何确定?
12、tcp如何应对服务端和客户端处理数据快慢不一的问题?
13、tcp如何提高网络吞吐量?
14、tcp是如何应对网络波动的?
15、tcp的的ack包可以携带数据吗?
16、讲一讲tcp的粘包、半包问题以及如何解决?
17、tcp异常情况?
17、基于tcp的应用协议有哪些?
18、tcp和udp的区别?
TCP协议全称传输控制协议,用于进行数据端到端的可靠传输,其可靠性含义为:接收方收到的数据是完整,有序,无差错的。tcp协议是全双工的,这意味数据能在两个方向上独立地进行传输,即客户端和服务端都可以发送和接受数据,都是发送端也都是接收端。
下图为TCP报文段示意图,如图,每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。
下图为tcp包的首部示意图:
tcp包的首部详细解释
校验和由发送端计算,然后由接收端验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃。
TCP在计算检验和时,要加上一个12字节的伪首部。但是伪首部并非TCP&UDP数据报中实际的有效成分,它是一个虚拟的数据结构,其中的信息是从数据报所在的IP分组头中提取,既不向下传送也不向上递交,而仅仅是为计算校验和。
伪首部共有12字节,包含IP首部的一些字段,有如下信息:32位源IP地址、32位目的IP地址、8位保留字节(置0)、8位传输层协议号(TCP是6,UDP是17)、16位TCP报文长度(TCP首部+数据)。
伪首部是为了增加TCP校验和的检错能力:通过伪首部的目的IP地址来检查TCP报文是否收错了、通过伪首部的传输层协议号来检查传输层协议是否选对了。
校验和计算方式:https://www.cnblogs.com/zxiner/p/7203192.html
TCP 协议为了实现可靠传输,通信双方需要判断自己已经发送的数据包是否都被接收方收到,这里不仅仅要确认每一个包是不是都收到了,还要能够保证数据的有序性。为了实现这个需求,就有了序列号(sequence number)和确认号(acknowledgement number),TCP将每个字节的数据都进行了编号, 序列号就是用来标识报文段中的的第一个数据字节,接收端收到数据包之后用序列号加数据长度再加1即可计算出发送端下次应该从什么位置发送数据,即确认序列号,每一个ACK包都带有对应的确认序列号,用于告知发送方我已经收到了数据,并且告诉发送方下一次发送数据从什么地方开始,这样一应一达的机制被称为确认应答机制。
在严格的一应一达机制下(假如没有滑动窗口),数据一定是顺序发送顺序接收的,比如发送端发送了一个序列号为1005的数据,那一定是接收端要求发送端发送序列号位1005的数据,也就意味着1005之前的数据都接收到了,如果接受端没有接收到数据或者接收到数据但是ack包丢失了,发送端会进行超时重传。
接收端ack标志位为1代表我已经收到数据了,而确认号是为了告诉发送方下一次发送数据应该从什么地方开始发,只有ack标志位为1才代表当前数据有效,所以一般情况下TCP协议已经建立连接开始发数据之后ack总是1。
TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端都必须保持各自方向上的传输数据序号。
第一次握手:客户端向服务器发出连接请求报文,此时报文首部中的同步标志位SYN=1,SYN的含义就是请求建立连接,所以实际上就是客户端请求向服务端建立连接。这个过程同时发送的还有客户端向服务端方向的初始序列号x,发送完之后客户端就进入了SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
第二次握手:服务器收到请求报文后,经过校验和如果同意连接,则发出确认报文。确认报文中的ACK=1,代表同意客户端到服务端方向上的连接;SYN=1,代表申请服务端向客户端方向的连接;确认序列号是x+1,代表下一次客户端发送数据要从x+1的序列号开始发送;同时也要选择一个服务端到客户端方向的初始序列号y。发送完之后服务端进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。可以看到第二次握手实际上干了两件事情,一是确认客户端到服务端的连接,二是申请服务端到客户端的连接。
第三次握手:TCP客户端收到服务端发来的确认和申请后,也要向服务器发出确认,确认报文的ACK=1,代表同意服务端到客户端的连接;确认序号是y+1,代表客户端期望服务端下次发送数据从y+1开始。由于服务端期望客户端此次发送数据的序列号为x+1,所以客户端还会发送自己的序列号x+1。一般情况下第三次握手是不会携带数据的,所以数据长度是0,但是要注意,理论上TCP协议允许第三次握手携带数据。由于两个方向上的连接建立都已经申请,所以第三次握手不用再发送SYN了。
这个报文发送出去之后,客户端进入ESTABLISHED(已建立连接)状态,当服务器收到客户端的确认后也进入已建立连接状态,此时TCP连接才真正建立,之后双方就可以开始通信了。
注意:当客户端发送了第三次握手的报文,客户端就进入了已建立连接的状态(这个时候客户端已经可以发送数据了),但是在服务端还没收到这个报文之前,服务端还没有进入已建立连接的状态,这个时候TCP连接其实还没真正建立。
注意:
一般所有ack包都是不携带数据的,只有开启了捎带应答才会在发送数据包的同时带上应答信息。三次握手过程中的前两次由于还处于建立连接阶段,是明确规定不能发送数据的,第三次握手客户端实际上已经建立连接了,理论上可以发送数据,但一般也不会发送数据,只有特别特殊的情况才会发送数据,特殊情况:https://blog.csdn.net/weixin_34198797/article/details/90687285 )
模拟过程:
发送过程 | 报文内容 | 客户端的序列号和确认序列号 | 服务端的序列号和确认序列号 | 数据长度 |
---|---|---|---|---|
第一次握手(客户端向服务端) | SYN=1 | 客户端选择的初始序列号:x=1257 | ||
第二次握手(服务端向客户端) | SYN=1,ACK=1 | 服务端的确认序列号:x=1258(服务端期望客户端下次发送的序列号) | 服务端选择的初始序列号:y=5336 | |
第三次握手(客户端向服务端) | ACK=1 | 客户端发送的序列号:x=1258(客户端向服务端发送服务端期望的序列号) | 客户端的确认序列号:y=5337(客户端期望服务端下次发送的序列号) | len=0 |
客户端向服务端发送数据 | ACK=1 | 客户端发送的序列号:x=1258(客户端向服务端发送服务端期望的序列号) | 客户端的确认序列号:y=5337(客户端期望服务端下次发送的序列号) | len=100 |
服务端向客户端应答 | ACK=1 | 服务端的确认序列号:x=1359(服务端期望客户端下次发送的序列号) | 服务端发送的序列号:y=5337(服务端发送客户端期望的序列号) | len=0 |
客户端向服务端送数据 | ACK=1 | x=1359 | y=5437 | len=50 |
服务端向客户端应答 | ACK=1 | x=1410 | y=5437 | len=0 |
服务端向客户端发送数据 | ACK=1 | x=1410 | y=5437 | len=100 |
客户端向服务端应答 | ACK=1 | x=1410 | y=5538 | len=0 |
当客户端端收到服务端的SYN+ACK报文(第二次握手报文包)后,会发送ACK包给服务端,然后自己的状态变为ESTABLISHED(已建立连接)。如果这个ACK包在网络中丢失,那服务端的状态还是SYN_RECV(同步收到状态),所以服务端会依次等待3秒、6秒、12秒之后重新发送SYN+ACK包,以便客户端能重新发送ACK包。服务端重发SYN+ACK包的次数可以通过设置/proc/sys/net/ipv4/tcp_synack_retries修改,默认值为5。如果重发指定次数后,仍然未收到ACK应答,那么一段时间后服务端会自动关闭这个连接。(一句话概括:服务端重发SYN+ACK包)但是由于客户端已经是已建立连接的状态,这个时候客户端收到了服务端的syn+ack包就会认为连接建立失败了,会回给服务端一个RST包,然后重新建立连接。
由于客户端已经处于ESTABLISHED(已建立连接)状态,是可以发送数据包的,这个时候如果数据包先于ack包到了,服务端会回应一个RST包,表示连接还未建立好,要求客户端重新发起连接(重新开始三次握手)。
这个问题可以从多个维度进行解读:
1)TCP连接是全双工的,两个方向都要建立连接,第一次握手是客户端向服务端发起请求的过程,第二次握手是服务端同意客户端向服务端方向连接并且申请服务端向客户端方向连接的过程。如果只有这两次握手,其实只建立了客户端向服务端方向的连接,但是服务端向客户端方向的连接没有建立起来。只有第三次握手,也就是客户端同意了服务端向客户端方向上的连接也建立起来才算完成全双工连接。
2)三次握手是为了确认双方的初始序列号。为了实现可靠数据传输,TCP协议的通信双方,都必须维护一个序列号,三次握手的过程就是通信双方相互告知序列号起始值,并且确认对方已经收到了序列号起始值的过程,如果只是两次握手,最多也就是客户端的序列号能够被服务端确认,而服务端的序列号是得不到客户端确认的。
3)如果只有两次握手,也就是说客户端向服务端发送连接请求(第一次握手),服务端收到了请求就建立客户端向服务端方向的连接,服务端发送一个确认包(第二次握手),客户端收到之后就建立服务端向客户端方向的连接,假如客户端发送的第一次握手的包发生了网络延迟,客户端又发送了一次,之后第二次发送的包顺利走完了全过程,连接已经关闭。服务端继续监听端口,这个时候网络延迟的那个申请包到了,服务端又会做出回应,只要做出回应,又会进入到建立连接的过程。
第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。连接释放数据报文首部中的FIN=1,代表申请关闭客户端向服务器方向的连接;其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时客户端进入FIN-WAIT-1(终止等待1)状态。TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
第二次挥手:服务器收到连接释放报文,会发出确认报文,确认报文的ACK=1,代表同意客户端向服务器方向的连接关闭;确认序号为u+1,并且带上自己的序列号seq=v,此时服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
客户端收到服务器的确认请求后,客户端就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最终数据,因为服务端到客户端方向的连接还没关闭)
第三次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,报文的FIN=1,代表申请关闭服务端向客户端方向的连接;确认序号为v+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
第四次挥手:客户端收到服务器的连接释放报文后,必须发出确认,确认报文的ACK=1,代表同意服务端向客户端方向的连接关闭;确认序号为w+1,而自己的序列号是u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(报文段一来一回传输的最大时长)的时间后,这个就是2MSL计时器,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。(一句话总结:服务器重发FIN请求)
TCP设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
参考:
https://blog.csdn.net/lengxiao1993/article/details/82771768
https://blog.csdn.net/sinat_36629696/article/details/80740678