运输层入门(四) TCP 协议的具体流程

127 阅读4分钟

在学习完成 TCP 的机制之后,我们可以对于 TCP 数据包的具体流程进行分析,相较于只管发送的 UDP,这复杂了许多。

三次握手:TCP 连接的建立

在真正发送应用层数据包之前,我们的客户端往往需要先和我们的服务器约法三章,就初始序列号等问题进行协商,同时在最初的沟通之中,判断通信管道的畅通与否。

TCP 数据包的基本结构

网上的许多文档喜欢放图,而不是放出描述数据包的代码,在我看来,对于结构简单的 UDP 而言,确实可以,而对于复杂的 TCP 数据包而言,则不太合适。

可以看到,TCP 有着许多的设置选项与许多的填写信息(这些纷繁复杂的数据类型,你可以理解为描述范围不一的整数)。

struct tcphdr 
{
    __be16 source;/* 我们主机的地址 */
    __be16 dest;/* 目标主机的地址 */
    __be32 seq;/* 序号 */
    __be32 ack_seq;/* 应答序号 */
    /* 以下都是标志位,我们需要结合实际情况进行判断  */
    __u16   res1:4,
            doff:4,
            fin:1,/* FIN 位 “四次挥手" */
            syn:1,/* SYN 位 “三次握手" */
            rst:1,
            psh:1,
            ack:1,/* ACK 位 响应包 */
            urg:1,
            ece:1,
            cwr:1;
    __be16 window;/* 窗口缓冲区大小 */
    __be16 check;
    __be16 urg_ptr;
};

第一次握手:客户端程序发送 SYN 数据包

“客户端-服务器"模型要求我们的客户端率先发起沟通,而在 TCP 协议中,率先发起沟通的客户端,就会以 SYN 数据包作为一切的起始点。

流程也是非常简单的,可以看到我们预先已经在上面的代码里面做出了简单的注释,找到 SYN 那一位,将其设置为 1(置位),再设置一个初始的序号,进而将这个完成初步设置的数据包经网络层发送至目标主机。

第二次握手:来自服务器的第一次响应(ACK 数据包)

在接收到来自于客户端的 SYN 数据包之后,我们的服务器就将对我们客户端的相关信息进行记录,并构造出一个对应的 ACK 数据包(将 ACK 置位),设定服务器方面的初始序列号,发送给客户端。

第三次握手:来自于客户端的确认

如果只是一问一答,就容易出现一个问题:有一些僵尸流量(之前的冗余数据包算是其中一种)可能会因为种种因素到达我们的服务器,如果不加入这一确认的流程,我们的服务器就会一直维持着那些僵尸流量对应的资源,进而造成严重资源浪费。

确认的过程非常简单,构造数据包加以响应即可。

之后,我们就可以依照正常的流程进行双向交流,这就需要灵活的判断,不存在公式化的流程一说。

四次挥手:TCP 连接的结束

对于我们的客户端或是服务器而言,TCP 连接的结束都可以用四次挥手加以解决。

第一次挥手:发送 FIN 报文

想要结束连接的一方发起 FIN 报文到另一方。

第二次挥手:受到 FIN 报文的一方加以响应

如题所示。

之后的历程:取决于发起方是客户端还是服务器

  • 对于我们的发起方是服务器而言
    在完成 FIN 报文的发送之后,我们的服务器也就中断了连接,在这个基础之上,我们的客户端想要进行数据写入,就会引发异常,从而导致我们的 TCP 连接强行终止。
    换而言之,在服务器主动终止的情况下,TCP 连接的中断是依靠异常而不是完整的四次挥手而结束的。
  • 对于我们的发起方是客户端而言
    在自己主动发送 FIN 到服务器并得到服务器的确认之后,我们需要静心等待服务器的 FIN 报文,这就是第三次握手。
    而当我们收到来自服务器的 FIN 报文之后,我们需要对此加以响应,这就是第四次握手。

评价:TCP 适合于可靠性高的网络应用

对于我们的应用程序而言,提供诸多保障的 TCP 协议,往往可以有效地提升我们应用的数据传输可靠性,因此 TCP 适合于可靠性要求较高的网络应用,但是对于实时性要求高的应用而言,这往往意味着资源的浪费。