TCP是什么
贴一段wiki对TCP的定义:传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。
为什么要有TCP
因为网络层IP协议是不可靠的,它不保证网络包的交付,不保证网络包的按序,也不保证网络包中数据的完整性。在需要保证网络数据的完整可靠时则需要上层的TCP协议来实现。TCP协议则是通过完整的TCP链接流程及各种动作来实现数据包的可靠性
TCP链接是怎么样的
借用课程上的流程图:
TCP链接可大致分为三个大部分:
- 三次握手阶段
- 链接建立阶段
- 四次握手阶段
TCP三次握手流程:
TCP链接是双向的,但是为了方便表述,还是使用常见的C-S模型。
- 第一次握手:一开始Client处于CLOSED状态。Client随机初始化自己的序列号,并将SYN标志置位,调用下层的服务将其发送到Server端,同时进入SYN_SENT状态。
- 第二次握手:转到Server端,当启动服务时Server会从CLOSED状态进入LISTEN状态,该状态下会监听收到的网络连接包。当Server收到Client发送的SYN包时,会随机初始化自己的首个序列号,将其填入Seq中,同时将Ack设置成CLient发送的Seq+1,并将SYN和ACK置位。将网络包发送回CLient,同时状态从Listen转为SYN_RCVD。
- 第三次握手:当CLient收到Server传来的第二次握手包,会将Ack设置成收到的Seq+1,同时将ACK置位,发送给Server。随后Client进入到ESTABLISHED状态。
当Server收到Client发送的Ack包后也从SYN_RCVD状态进入到ESTABLISHED状态,这表明三次握手结束正式进入链接建立阶段。
TCP四次挥手流程
在连接建立阶段中,当一放发送了FIN标志置位的网络包时表明期待结束链接,随后进入四次挥手来保证链接的完整断开。为了方便表述,这里将先发送FIN包的一方称为CLient,另一方称为Server。
- 第一次挥手:Client在自己的数据发送完成后会在正常链接的网络包中将FIN标志置位,发送给Server。同时状态从ESTABLISHED转为FIN_WAIT_1。
- 第二次挥手:Server对收到第一次挥手网络包进行回复,并将状态从ESTABLISHED转为CLOS_WAIT。当Client收到回复包后将状态从FIN_WAIT_1转入FIN_WAIT_2。
- 第三次挥手:当Server将自己的数据发生完后会将FIN标志置位发送给Client,同时自己的状态转为LAST_ACK。
- 第四次挥手:当Client收到Server发放的FIN包后将自己的状态转为TIME_WAIT,同时对Server发送ACK回复包。
当Server收CLient的ACK包后状态转为CLOSED,Client在进入TIME_WAIT状态后经过2倍的MSL(Maximum Segment Lifetime 报文最大生存时间)时间后会进入CLOSED状态。此时TCP连接经过四次挥手正式关闭。
部分问题
为什么每次Ack总等于收到的Seq+1或者Seq+n?
因为Ack是指期待收到的下一个网络包的序列号。
为什么初始序列号是随机的而不是0?
初始序列号随机是为了防止网络环境中其它的网络包对链接建立产生影响。主要是防止历史报文被同样的四元组接受,以及网络黑客伪造报文。
但是当我们通过wireshark抓包会发现初始序列号都是0。这个问题困扰我挺久,经过查阅资料得知,wireshark为了方便阅读会将序列号显示为初始序列号的相对值,通过设置可以使其显示真实的序列号。
为什么挥手是四次,三次可不可以?
挥手阶段不一定是四次,在部分情况下可以缩减为三次。
当Server收到CLient发生的第一次挥手报文时,如果Server发现自己也没有更多数据需要发送时会向CLient发送FIN+ACK报文。CLient在收到该报文后会进入CLOSING状态而不是FIN_WAIT_2状态。然后会对该报文进行回复并等待进入TIME_WAIT。
即将第二次挥手和第三次挥手收缩到个TCP报文中,只进行一次挥手。并且CLient不会进入TIME_WAIT_2状态而是CLOSING状态。
为什么要有TIME_WAIT状态,并且还要等到2个MSL
设置TIME_WAIT状态是为了防止历史链接中的数据被后面同样的四元组链接接受。Linux中一般设置MSL=30s。2个MSL即是一个网络包一来一回所需要的最长时间。这个时间足以让两个方向上的数据包都被丢弃,使得原来的链接完全结束不对后面的链接造成影响。