• TCP 连接


    面试题传送

    TCP 报文格式

    此处介绍建立或者断开TCP连接时,需要了解的TCP报文段首部字段含义:

    序列号 seq:占4个字节(32位),用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号 seq 就是这个报文段中的第一个字节的数据序号。
    确认号 ack:占4个字节(32位),期待收到对方下一个报文段的第一个字节的数据序号;序列号表示报文段携带数据的第一个字节的序号;而确认号指的是期望接收到下一个字节的序号;因此当前报文段最后一个字节的序号 +1 即为确认号。

    TAG Description
    URG 紧急指针标志,为 1 时表示紧急指针有效,为 0 则忽略紧急指针
    ACK 确认序号标志,为 1 时表示确认号有效,为 0 表示报文中不含确认信息,忽略确认号字段
    PSH PUSH 标志,PUSH 标志为 1 的 TCP报文段,接收方在接收到该 TCP报文段 以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队
    RST 重置连接标志,用于重置由于主机崩溃或其他原因(拒绝非法的报文段或连接请求)而出现错误的连接
    SYN 同步序号标志,用于建立 TCP 连接,在连接请求中,SYN=1,ACK=0;而连接响应中,SYN=1,ACK=1
    FIN 断开连接标志,用于断开 TCP 连接,在断开连接请求中,FIN=1,ACK=0;而断开连接响应分两个步骤(需要传完全部数据),第一部分:FIN=0,ACK=1,第二部分:FIN=1,ACK=1

    TCP 三次握手


    第一次握手:建立连接时,客户端发送同步 SYN 包(seq=x)到服务器,并进入 SYN_SENT 状态,等待服务器确认;

    第二次握手:服务器收到同步 SYN 包,必须返回给客户端一个确认 ACK(ack=x+1),同时自己也发送一个同步 SYN(seq=y),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;

    第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认 ACK 包(ack=y+1),此包发送完毕,客户端和服务器进入 ESTABLISHED(TCP连接成功) 状态,完成三次握手 。

    TCP 三次挥手


    第一次挥手:客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为 seq=u(等于前面已经传送过来的数据的最后一个字节的序号加 1),此时,客户端进入 FIN-WAIT-1 状态。 TCP规定,FIN 报文段即使不携带数据,也要消耗一个序号;
    第二次挥手:服务器收到连接释放报文,发出确认报文 ACK=1,ack=u+1,并且带上自己的序列号 seq=v,此时,服务端就进入了 CLOSE-WAIT(关闭等待)状态。服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间;
    第三次挥手:客户端收到服务器的确认请求后,此时,客户端就进入 FIN-WAIT-2 状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据);
    第四次挥手:服务器将最后的数据发送完毕后,就向客户端发送连接释放报文 FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w,此时,服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认;
    四次挥手后的等待(确保服务端收到第四次挥手TCP报文):客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是 seq=u+1,此时,客户端就进入了 TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过 2MSL(最长报文段寿命)的时间后,客户端才会撤销相应的 TCB(Thread Control Block)线程控制块,进入 CLOSED 状态。而服务器只要收到了客户端发出的确认,立即撤销 TCB,进入 CLOSED 状态,结束这次 TCP 连接 。可以看到,服务器结束 TCP 连接的时间要比客户端早一些 。

    常见面试题

    【问题1】为什么建立连接的时候是三次握手,端口连接的时候却是四次握手?
    (因为断开连接时,需要传完当前连接剩余的数据,而建立连接时,并不需要此步骤)
    答:因为当 Server端 收到 Client端 的SYN连接请求报文后,可以直接发送 SYN+ACK报文。其中 ACK报文 是用来应答的,SYN报文 是用来同步的。但是关闭连接时,当Server端收到 FIN报文 时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK报文,告诉 Client 端,“你发的FIN报文我收到了”。只有等到我 Server 所有的报文都发送完了,我才能发送 FIN报文,因此不能一起发送。故需要四步握手。

    【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
    (为了确保 Server 收到了 ACK 报文段)
    答:虽然按道理,四个报文都发送完毕,我们可以直接进入 CLOSE 状态了,但是我们必须假想网络是不可靠的,有可以最后一个 ACK报文 丢失。所以 TIME_WAIT 状态就是用来重发可能丢失的 ACK报文。在 Client 发送出最后的 ACK报文时,该ACK报文丢失。Server如果没有收到 ACK报文,将不断重复发送 FIN报文段。所以 Client 不能立即关闭,它必须确认 Server 接收到了最后的 ACK报文。Client会在发送出 ACK报文之后进入到 TIME_WAIT 状态。Client 会设置一个计时器,等待 2MSL 的时间。如果在该时间内再次收到 FIN报文,那么 Client 会重发 ACK报文 并再次等待 2MSL 的时间;所谓的 2MSL 是两倍的 MSL(Maximum Segment Lifetime),MSL 指一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果等待了 2MSL 之后,Client 没有再次收到 FIN,那么 Client 推断 ACK报文 已经被 Server 成功接收,则结束 TCP 连接。

    【问题3】为什么不能用两次握手进行连接?
    (假设两次握手,可能发生两种异常情况)
    答:三次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
    现在把三次握手改成仅需要两次握手,考虑计算机 Server 和 Client 之间的通信,假定 Client 给 Server 发送一个连接请求(SYN=1)分组,Server 收到了这个分组,并发送了确认应答(SYN=1,ACK=1)分组。按照两次握手的协定,Server 认为连接已经成功地建立了,可以开始发送数据分组 。但此时可能会有两种异常情况:

    ①.Client 的请求分组(SYN=1)在传输中被堵塞(并非丢失)的情况下,由于请求分组在网络中滞留的时间过长,Client 很久得不到 Server 的应答分组,将会发起第二个请求分组(SYN=1),Server 收到了第二个请求分组,即刻返回确认分组(SYN=1,ACK=1),建立连接;但第一个请求分组(SYN=1)并没有丢失(对于 Client 而言此请求分组已经丢失或者失效),在连接释放以后的某个时间才到达 Server,此时 Server 会误认为是 Client 又发出的新的建立连接请求,于是又返回确认分组(SYN=1,ACK=1),又建立了连接,但是 Client 实际上并没有发出请求分组(SYN=1),不会理睬 Server 的确认分组(SYN=1,ACK=1),也不会向 Server 发送数据,而 Server 却以为新的连接已经建立,并一直等待 Client 发送数据,这样就白白浪费了 Server 的许多资源 。
    ②.Server 的应答分组(SYN=1,ACK=1)在传输中被丢失的情况下,Client 将不知道 Server 是否已准备好,不知道 Server 建立什么样的序列号,Client 甚至怀疑 Server 是否收到自己的连接请求分组。在这种情况下,Client 认为连接还未建立成功,将忽略 Server 发来的任何数据分组,只等待连接确认应答分组 。而 Server 在发出的分组超时后,重复发送同样的分组 。这样就形成了死锁 。

    【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
    (保活计时器)
    TCP连接设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接 。

  • 相关阅读:
    五子棋算法
    记录2个算法知识(常见面试题)
    python基础面试题之类型转换(字典篇)
    Python中的多线程和多进程的应用场景和优缺点。
    python基础面试题之输入年月日,判断这个日期是这一年的第几天。
    Python装饰器(面试题)
    linux终止进程
    nest_asyncio出现错误
    sanic 相关的插件
    linux如何配置nginx全局变量
  • 原文地址:https://www.cnblogs.com/zzzwqh/p/13053519.html
Copyright © 2020-2023  润新知