三次握手与四次挥手
1.什么是可靠性?
1.具有应答,可以保证对历史数据的可靠性
2.通信中,最新的报文永远没有应答,最新可靠性无法保证
如果要保证可靠性,需要在tcp中把确认应答机制放在核心位置



总结:
TCP保证可靠性的核心是确认应答机制,可靠性的本质是对历史消息的确认应答
客户端给服务器发消息,服务器就必须要应答,这个应答是由对方操作系统发的
通常是并行发的,串行发消息效率太低了
TCP传递报文传递的是带有TCP报头的报文,应答是指只带有TCP报头
流量控制
一台主机接收数据的大小由接收缓冲区剩余大小空间来做决定
我们所构建的报文都是给对方的
流量控制是为了合理地控制流量,就好像每个人接收能力有差异需要动态调整
TCP 头部结构的定义:
C++
struct tcphdr {
__be16 source; /* 源端口号 */
__be16 dest; /* 目的端口号 */
__be32 seq; /* 序列号 */
__be32 ack_seq; /* 确认号 */
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4, /* 保留位 */
doff:4, /* 数据偏移(头部长度) */
fin:1, /* FIN 标志 */
syn:1, /* SYN 标志 */
rst:1, /* RST 标志 */
psh:1, /* PSH 标志 */
ack:1, /* ACK 标志 */
urg:1, /* URG 标志 */
ece:1, /* ECN-Echo 标志 */
cwr:1; /* 拥塞窗口减少标志 */
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4, /* 数据偏移(头部长度) */
res1:4, /* 保留位 */
cwr:1, /* 拥塞窗口减少标志 */
ece:1, /* ECN-Echo 标志 */
urg:1, /* URG 标志 */
ack:1, /* ACK 标志 */
psh:1, /* PSH 标志 */
rst:1, /* RST 标志 */
syn:1, /* SYN 标志 */
fin:1; /* FIN 标志 */
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window; /* 窗口大小 */
__sum16 check; /* 校验和 */
__be16 urg_ptr; /* 紧急指针 */
};
标志位本质是报头的比特位
为什么需要标志位?
TCP接收方会有不同类型的报文,针对不同类型,需要有不同的做法,要有表示报文类型的字段,需要标志位


每一个 ACK 都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发由你自己决定.

那么, 如果超时的时间如何确定?
• 最理想的情况下, 找到一个最小的时间, 保证 "确认应答一定能在这个时间内返回".
• 但是这个时间的长短, 随着网络环境的不同, 是有差异的.
• 如果超时时间设的太长, 会影响整体的重传效率;
• 如果超时时间设的太短, 有可能会频繁发送重复的包;
TCP 为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间.
• Linux 中(BSD Unix 和 Windows 也是如此), 超时以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是 500ms 的整数倍.
• 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
• 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
• 累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接.

相关习题:
1.主机甲向主机乙发送一个(SYN=1,seq=11220)的TCP段,期望与主机乙建立TCP连接,若主机乙接受该连接请求,则主机乙向主机甲发送的正确的TCP段应该是()
A.(SYN=1,ACK=1,seq=11220,ack=11220)
B.(SYN=1,ACK=1,seq=11221,ack=11221)
C.(SYN=0,ACK=0,seq=11221,ack=11221)
D.(SYN=0,ACK=1,seq=11220,ack=11220)
SYN和ACK是TCP协议报头中的6个保留位中的2位。
ACK:ACK位置为1表明确认号是合法的。如果ACK为0,那么数据报不包含确认信息,确认字段被省略。
seq:发送方的本条报文起始序号
ack:对于上次对方发送的数据的确认序号,通常是对方发送的数据的起始序号+数据长度,但是在三次握手阶段稍有不同,是上次对方发送的请求的起始序号+1
SYN=1,seq=11220, SYN=1表示这是一个客户端的第一次握手连接建立请求, 并且起始序号为11220
服务器收到请求后,向客户端进行第二次握手,第二次握手既是自己的连接建立请求,也是对客户端连接请求的确认,因此 SYN=1,ACK=1, 而确认序号为对方的起始序号+1, 则为11221
则 SYN=1,ACK=1,ack=11221,seq=自己的起始序号,一般为随机值这个不做要求
则符合条件的只有B选项
2.下列哪项最恰当地描述了建立TCP连接时"第一次握手"所做的工作 ( )
A."连接发起方"向"接收方"发送一个SYN-ACK段
B."接收方"向"连接发起方"发送一个SYN-ACK段
C."连接发起方"向目标主机的TCP进程发送一个SYN段
D."接收方"向源主机得到TCP进程发送一个SYN段作为应答
TCP三次握手过程:
- 第一次握手:客户端向服务器发送连接请求, 也就是SYN段
- 第二次握手:服务器向客户端发送确认回复及连接请求, 也就是 SYN+ACK段
- 第三次握手:客户端向服务器发送确认回复,也就是ACK段
基于以上理解,正确选项为:C
【多选题】客户端主动断开TCP连接的时候,以下"四次挥手"过程中状态变迁表述正确的是()
A.Client发送一个FIN,用来关闭Client到Server之间的数据传输,Client进入FIN_WAIT1状态
B.Server收到了来自Client的FIN包,发送一个ack给client,进入CLOSE_WAIT状态
C.Server发送一个FIN,用来关闭Server到Client之间的数据传输,Server进入LAST_ACK状态
D.Client收到FIN包之后,Client发送一个ACK给Server, 紧接着进入TIME_WAIT状态,Server收到ack后进入CLOSED状态
【多选题】客户端主动断开TCP连接的时候,以下"四次挥手"过程中表述错误的是()
A.当Client收到Server的ACK包之后,Client状态变成FIN_WAIT2状态
B.当Server发送FIN包到Client之后,Client需要等待1MSL,状态才从TIME_WAIT状态变成CLOSED状态
C.Server端出现大量的CLOSE_WAIT状态,是由于Client没有及时的关闭连接
D."四次挥手"是完全没有必要的,"三次挥手"就可以了
A正确
B错误:当Server发送FIN包到Client之后,Client需要等待2MSL,处理有可能因为最后一次ACK丢失导致的重传(重传的FIN是1MSL,自己发送的ACK是1MSL,共2MSL),最终状态才从TIME_WAIT状态变成CLOSED状态
C错误:CLOSE_WAIT状态是被动关闭方收到FIN并进行ACK回复后进入的状态,接下来他会等待上层知道对方要关闭连接后做出处理,当自己也要关闭连接的时候给对方发送FIN,则 进入LAST_ACK状态,而一旦自己上层没有做出处理,则套接字状态会一直处于CLOSE_WAIT, 因此Server端出现大量的CLOSE_WAIT,是由于Server端没有及时的关闭连接导致的。
D错误:这里需要了解FIN的功能,收到FIN只能表示对方不再给自己发送数据,而不是完全关闭既不发送数据也不再接收数据,他还是可以继续接收数据的,因此被动关闭方收到FIN请求进行ACK确认后,可能还会继续发送数据,直到自己也不再发送了,才会发送接下来的FIN包,因此四次挥手不能合并成为 三次挥手。
总结:
1. 三次握手:建立可靠的双向通道
- 第一次握手(Client -> Server) :发送
SYN=1, seq=J。Client说:"我要建立连接,我的初始序列号是J。" - 第二次握手(Server -> Client) :发送
SYN=1, ACK=1, seq=K, ack=J+1。Server说:"我同意连接(ACK),我的初始序列号是K。同时,你发的J我收到了,下次请你从J+1开始发。" - 第三次握手(Client -> Server) :发送
ACK=1, ack=K+1。Client说:"好的,你发的K我也收到了,下次从K+1开始。"
为什么是三次?
两次握手只能保证Client知道Server的收发能力正常,但Server无法确认Client的接收能力是否正常。第三次握手就是为了让Server确认这一点,从而建立起一个可靠的双向通信通道。
2. 四次挥手:优雅地关闭双向通道
TCP连接是全双工的,意味着双方可以独立地发送和接收数据。因此,关闭连接也需要双方各自关闭。
- 第一次挥手(Client -> Server) :
FIN=1。Client说:"我数据发完了,要关闭我这边到你的发送通道了。"(Client进入FIN_WAIT_1) - 第二次挥手(Server -> Client) :
ACK=1。Server说:"哦了,知道你关发送通道了。"(Server进入CLOSE_WAIT,Client进入FIN_WAIT_2)注意:此时Server可能还有数据要发给Client,所以它的发送通道还开着。 - 第三次挥手(Server -> Client) :
FIN=1。Server也发完了所有数据,说:"我也发完了,我也要关闭我这边到你的发送通道了。"(Server进入LAST_ACK) - 第四次挥手(Client -> Server) :
ACK=1。Client说:"收到,再见!"(Client进入TIME_WAIT,等待2MSL后关闭;Server收到ACK后直接关闭)
为什么是四次?
第三次挥手(Server -> Client) :FIN=1。Server也发完了所有数据,说:"我也发完了,我也要关闭我这边到你的发送通道了。"(Server进入LAST_ACK)
- 第四次挥手(Client -> Server) :
ACK=1。Client说:"收到,再见!"(Client进入TIME_WAIT,等待2MSL后关闭;Server收到ACK后直接关闭)
为什么是四次?
因为中间可能有一个"等待数据处理并发送完毕"的间隔期(CLOSE_WAIT状态)。这无法与第二次挥手的ACK合并 ,所以需要四次。

