传输层协议TCP(1)

1 TCP报文结构

        TCP 是一种面向连接的、可靠的、基于字节流的端到端的传输层通信协议(RFC 793)。其报文结构如下。

     可以看到,TCP 报文位于 IP 报文头之后,从 IP 的视角来看,TCP 报文属于 IP 的数据(对应着 IP Header 中的“协议”字段等于6)。

     进一步打开报文的结构,如下所示:

(1)源目端口号

       源端口号、目的端口号的数据类型都是无符号整数,各占据 16 bits,所以其取值范围都是 0~65535(64K)。需要强调的是,1个端口号与1个进程挂钩,但是它并不等于该进程的进程 ID。此外,需要明确指出是应用层哪个进程数据交给传输层(发送出去),传输层接收到的数据,又得交给应用层哪个进程。这就引出了端口号的概念。

       TCP/UDP 是如何知道端口号与其背后的进程之间的绑定关系的呢?这个设计socket套接字这个概念:

       

      上图所表达的是一种编程层面的概念:socket 是对 TCP/UDP 的一种抽象和封装,它对外提供了编程接口。

总结来说:

  1. TCP 的目的是将数据从一个主机的某个进程传递到另一个主机的某个进程。
  2. 标识主机的是 IP 地址,而 IP 地址体现在 IP 报文头的字段中(源 IP/目的 IP)。
  3. 标识进程的就是 TCP 报文头中的字段“源端口号/目的端口号”,端口号与进程之间的绑定关系,通过 socket 编程接口设置。
  4. 标识1个 socket,是两个元素:IP 地址、端口号(背后是1个进程);

(2)数据偏移量(Data Offset)

       Data Offset,占位4个 bits,指的是 TCP 数据从哪开始,单位是4个字节(32 bits)。这个字段的含义,其实也就是表达的是 TCP 头部的长度(单位是4字节):数据的偏移量,潜台词就是 TCP 头部的长度

(3)保留(Reserved)

       Reserved 字段,顾名思义就是保留给将来使用,当前其值必须为0。

(4)标志位(Flags)

       RFC 793 定义了6个标志位,RFC 3168 又增加了2个,一共8个,每个标志位占用1个 bit。TCP标志位的各个代号意思如下表:

      

(5)校验和(Checksum)

       TCP 的校验和与 IP 的校验和的算法是一样的,只是所计算的内容不同。TCP 校验和计算的内容包括:TCP 伪头部、TCP 头部、TCP 数据。

       

      TCP 伪头部(TCP Pseudo Header)并不是一个真正存在的 Header(不会在网络上传输),只是 TCP 为了计算校验和时所引入的一个数据结构,所包含的参数如下:

   

(6)选项(Options)

       TCP Header 中的选项从数据结构的角度来讲,分为两大类型,如下图:

      

      第一类数据结构就是1个单字节(8  bits),只包含 option-kind。第二类数据结构是多字节,包括:option-kind(8 bits)、option-length(8 bits)、option-value(变长)等3部分,其中 option-length 的单位是字节,包括“option-kind、option-length、option-value”三者加在一起的长度。

      常用的 TCP Options,如下表:

    

(7)MSS(Maximum Segment Size,最大Segment长度)

      

      MSS 指的是在 TCP 的过程中,TCP Data(不包含 TCP Header)的最大长度(长度单位是字节)。这个是创建 TCP 连接时,TCP 双方协商的一个参数。如果双方没有协商这一参数,理论上说,TCP 的双方可以发送/接收任意长度的 TCP Segment,不过在具体的实践中这也不可能——不仅不可能,双方还谨小慎微:TCP 默认的 MSS 是536(字节)。536字节,确实稍微有点保守,毕竟以太网的 MTU = 1500,从而可以推导出 TCP Segment 的长度为1460字节(1500 - 20(IP Header 长度,不包含 IP Options) - 20(TCP Header 长度,不包含 TCP Options))。所以,IPv4 网络中的 MSS 典型取值是 1460(IPv6 是 1440)。

      最后补充一点,由于 MSS 占用2个字节(16 bits,参见图5-9),所以 MSS 的最大值是 65535(64K),考虑到 IPv4 的 MSS 的典型值仅仅是1460,这已经是足够的。

(8)TCP-MD5 & TCP-AO

       TCP-MD5 和 TCP-AO 的主要目的都是用于防止TCP欺骗攻击(TCP Spoofing Attacks)。MD5(MD5 Message-Digest Algorithm,消息摘要算法)是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

       TCP-MD5数据结构如下所示:

由于 MD5 digest 固定为16字节,所以 TCP-MD5 的 option-length 字段的值为18(字节)。TCP-MD5 所计算的内容包括:

  • (1)TCP 伪头部,与 TCP 校验和算法中的 TCP 伪头部相同
  • (2)TCP 头部,但是不包括 TCP 选项,而且假设 TCP 校验和为0
  • (3)TCP 数据
  • (4)一个 TCP 通信双方都知道的密钥

2 TCP连接

2.1 TCP连接创建的基本过程

       什么是TCP连接?很多资料里写TCP是一种面向连接的协议,但实际上官方并没有很好的去定义到底什么是TCP连接,只是通过代码上的实现来说明TCP连接。下面先说一下TCP连接创建的基本过程。TCP连接创建的过程如下图所示,经典的三次握手。

        

 

       上图中,A 给 B 发了2次报文,B 给 A 发了1次报文,这3个报文都有一个共同的特点:没有发送数据(报文只包含 TCP 报文头),因为它们的目的就不是为了发送数据,而是为了创建连接。即使报文中没有数据只有报文头,那报文头中的每个字段也应该赋上合适的值,不过从表达创建连接基本过程的角度,图5-16忽略了其中很多字段,只是专门展现了“源端口号、目的端口号、Sequence Number(序列号,简写:SEQ)、Acknowledgment Number(确认序列号,简写:AKN)、SYN(标记)、ACK(标记)”等6个字段。下面列出来这个几个字段:

       

  • 报文1:报文1是从 A(端口号为1000)发往B(端口号为2000),它的标记 SYN = 1(如无特别说明,其他标记都是为0。以下同)。标记 SYN 是“Synchronize sequence numbers”的简写,直接翻译就是“同步序列号”,背后的含义是:向对方请求建立连接。当 SYN = 1 的时候,SEQ(Sequence Number)就表示这个序列号是初始序列号(The Initial Sequence Number,ISN)。
  • 报文2:报文2是从 B(端口号为2000)发往A(端口号为1000)。报文2有两层含义。第1层含义是 B 对 A 请求建立连接的1个响应,它的标志位 ACK = 1,同时它的 AKN = 101。(AKN,Acknowledgment Number)。AKN 的意思是:你的报文我收到了,你的 SEQ 我也知道了,我期望你下一个报文的 SEQ 等于我发给你的 AKN(当然我发给你的 AKN 就是你这个报文的 SEQ + 1),所以我们看到报文2的 AKN = 1012层含义是 B 也对 A 表达了一个“创建连接”的请求,所以我们在报文2中看到:SYN = 1,SEQ = 300。这里的“300”也就是 B 的 ISN。当 A 收到 B 发过来的报文2时,会心一笑:B 接纳我了,所以我们在图5-16中看到 A 的状态是 ESTABLISHED,意思是:A 自己认为创建了连接。
  • 报文3:A收到B的应答报文(请求)后,A也再给B回复确认受到报文。从 A(端口号为1000)发往B(端口号为2000)。报文3的 ACK = 1,AKN = 301,根据前面的描述,我们知道这就表示 A 同意了 B 创建连接的请求。

           同时我们看到,报文3的 SEQ = 101,这是 A 对 B 当初应答报文的一个承诺:B 期望它的 SEQ = 101,那么 A 的 SEQ 就是 101。需要强调的是,此时报文3的 SYN = 0,因为 SYN 是“创建连接请求”的标记,而报文3并不是为请求创建连接,所以 SYN = 0。当 B 收到 A 的应答报文(报文3)后,也是会心一笑:A 接纳我了,所以我们在图5-16中看到 B 的状态是 ESTABLISHED,意思是:B 自己认为创建了连接。至此,经典的三次握手完成了。

       前文中出现的虚拟号、确认序列号、同步序列表等参数的解释如下:

        

       由于本小节的主题是“创建连接的基本过程”,所以我们忽略了其他字段。笔者以为,看一看实际的报文,对连接的创建能有一个更好的理解,对其他字段也会有个直观的认识。下图就是“三次握手”报文的抓包图。

       

       

       需要强调的是,图5-21之后并没有“TCP 连接创建第3次握手报文之 Options”,这是因为第1次握手时已经将 Options 发送到对方,所以第3次握手就没必要再重复了。

2.2 TCP 数据传输

       在讲述 TCP 数据传输之前,先讲述几个概念:

  • (1)TCP 传输数据,是以字节为单位,比如1次(1个报文)传输7个字节、53个字节、200个字节、1200个字节等等。所以,TCP 也被称为“基于字节流”的协议。
  • (2)TCP 为每一个字节都做了编号,体现这个编号的就是 TCP 报文头中的序列号(SEQ)字段。
  • (3)TCP 创建连接请求报文(SYN = 1),也会消耗掉1个编号。
  • (4)TCP 确认包报文(ACK = 1),不消耗编号。
  • (5)TCP 确认报文头中的确认序列号(AKN),表达的是期望对方下一个报文头中序列号(SEQ)所取的值。

       还是按照前面5-16图继续,TCP的数据传输过程如下:

       

        图5-23有1个最大的特点:A 向 B 发送数据,B 收到数据后给 A 回以1个确认报文。所以,我们在图5-23中看到4个报文:报文1是 A 向 B 发送数据,报文2 是 B 向发送确认报文(确认数据已经收到),报文3是 A 向 B 发送数据,报文4 是 B 向发送确认报文(确认数据已经收到)。

        上图中只保留了“源端口号、目的端口号、Sequence Number(序列号,简写:SEQ)、Acknowledgment Number(确认序列号,简写:AKN)、ACK(标记)”等5个字段。另外图5-22中的 DataLen 表达的是 TCP 数据长度,这个并不是 TCP Header 中的字段,而是计算所得(IP 报文头中的“总长度” - IP 报文头长度 - TCP 报文头长度)。将上图中的报文列成表如下所示: 

          根据前面的描述,ACK 报文不消耗编号,所以 A 第3次握手之后,紧接着向 B 发送数据的报文(表5-6中的报文1,也是图5-23中的报文1),它的序列号还是等于101(SEQ  = 101)。同时它的确认序列号也还是等于 1001(AKN = 1001),因为此时 B 还没有向 A 发送新的报文。

           TCP 为每一个字节都做了编号,体现这个编号的就是 TCP 报文头中的序列号(SEQ)字段。这句话有几个意思:

  • (1)TCP 报文头中的序列号(SEQ)字段,代表该报文中数据的第1个字节的编号。所以,报文1中的 SEQ = 101,就意味着该报文中数据的第1个字节的编号是101。
  • (2)报文1里的数据长度是 100(DataLen = 100),这也就意味着报文1里有100个字节的数据。

         基于前面的描述,我们会比较清楚地知道,报文3的 SEQ = 201,AKN = 1001(因为报文2仅仅是1个 ACK 报文,并没有传输数据)。同理,我们也会得到报文4 的 SEQ = 1001,AKN = 301。

         前面说过,TCP 是一种面向连接的、可靠的、基于字节流的端到端的传输层通信协议。分析以下有以下几方面:

  1. 面向连接,这几个字正是本章希望说明的,只是现在还不到时候,我们后面再说。
  2. 端到端,是说 TCP 通信的两端分别是某一个主机的某一个进程。
  3. 基于字节流,前文已经说了,是因为 TCP 将每个字节都做了编号,也就是说 TCP 传输的数据单元是字节。
  4. 可靠,是什么意思呢?TCP 如何做到可靠传输呢?答案如下所述。

     TCP保证可靠传输的最基本三点如下所述:

  • (1)传输的结果是明确的,要么是成功,要么是失败。TCP 不保证传输绝对成功,因为它不能保证的因素太多,比如传输的过程中,网络断了,TCP 也无能为力,只能传输失败。但是 TCP 能够保证的是传输的结果是明确的,不能说不知道“到底是成功还是失败”
  • (2)TCP 确定传输是成功的机制就是:(A)给所发送的每个字节都做了编号;(B)通过“发送/确认”报文来明确告知传输成功
  • (3)如果有暂时的故障致使对方没有收到数据,TCP 通过重传机制来纠错。

       而要保证这3点,TCP 所基于的就是连接——TCP 连接。但是,TCP 连接到底是什么呢?RFC 793 并没有明确定义 TCP 连接是什么,反而是各个具体的实现以代码的形式,表达了 TCP 连接是什么(比如 Linux、比如 BSD、比如 windows)。由于不同的实现,其代码不尽相同。说到 TCP 连接,首先就得提到 socket,socket 的定义,如图5-26所示:

                             

       上面定义了 socket 最根本的两个元素:IP 地址、端口号。那么可以想象,TCP连接首先是一对 socket,如图5-27所示:

         

        图中的 socket、tcp_connection 都是主机中的数据结构(准确地说,是主机的进程(或者是内核)中的数据结构的实例(对象))。至此,我们看到了 TCP 连接的部分真相:

  • (1)每个 host 都会为一个人类所理解的 TCP 连接创建1个数据实例对象(tcp_connection)
  • (2)这个数据实例对象的值是两个 socket 实例,一个指代自己的 socket,一个指代对端的 socket。

        所以,我们在有的资料中看到,它们把 TCP 连接定义为一对 socket(<socket1, socket2>)。一对 socket 唯一确定1个 TCP 连接,但是1个 TCP 连接,远远不止一对 socket,它还有更重要的内容,如图5-28所示:

         

       TCB 是 TCP Control Block(TCP 控制快)的缩写,它包含了一对 socket,还有其它内容:seq_number(序列号)、ack_number(确认序列号)......其实说白了,这些所谓其它内容,可以简单理解为 TCP 报文头(TCP Header)的字段。

       至此,我们可以说看到了 TCP 连接的全部真相:

  • (1)TCP 连接就是主机内的数据结构的1个实例对象
  • (2)这个数据结构就是 TCB
  • (3)TCP 包含一对 socket,还包含 TCP 报文头中所对应的字段(含 TCP Options)。

       TCB 数据实例会给给每个字节记录对应的编号,但又不是真的记录。它只须根据公式计算这次数据传输以后,它下次传输时的序列号应该是什么(然后存储在 TCB 数据实例中),然后与对端发送过来的报文中的确认序列号相比较,如果两者想等,那说明该次传输的数据已经完全被对端完全接收了。

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值