5.2 TCP 连接
一直一来,很多资料都说 TCP 是一个面向连接的协议。对于不太了解 TCP 的人来说,这句话实际上造成了很大的误解。
TCP 所面向的连接,到底是什么?是网络中的一条链路吗?是图5-15所示的那条“A-R1-R3-R4-R6-B”链路吗?
图5-15 TCP 连接是网络中的链路吗?
实际上,TCP 所面向的连接,既不是网络中一条真实的物理链路也不是一条虚拟的链路,而且它其实根本就不是链路。
RFC 793所说的 TCP 连接,其英语单词是“connection”,而我们所说的网络链路(或者网络连接),其英语单词是“link”。笔者试图通过两个英语单词的区分来帮助理解 TCP 连接与网络连接的区别,不过也没看出个所以然。
RFC 33 关于 connection 与 link 的关系,是这样的描述的:A connection is an extension of a link。单独看这句话,还是让人一头雾水,不过它彻底打消了我试图通过英文单词来区分两个连接的念头,^_^
那么,所谓的 TCP 连接到底是什么呢?让我们先从 TCP 连接的基本创建过程说起。
5.2.1 TCP 连接的基本创建过程
TCP 连接的基本创建过程,如图5-16所示:
图5-16 TCP 创建连接基本创建过程
图5-16所表达的就是著名 TCP 连接创建的“三次握手”过程,我们先忽略图中的“TCP Client、TCP Server、CLOSED、LISTEN”等单词(下文会讲述),先看看主机A 与 主机B 为了创建连接而进行的3次通信(三次握手)。
图5-16中,A 给 B 发了2次报文,B 给 A 发了1次报文,这3个报文都有一个共同的特点:没有发送数据(报文只包含 TCP 报文头),因为它们的目的就不是为了发送数据,而是为了创建连接。
即使报文中没有数据只有报文头,那报文头中的每个字段也应该赋上合适的值,不过从表达创建连接基本过程的角度,图5-16忽略了其中很多字段,只是专门展现了“源端口号、目的端口号、Sequence Number(序列号,简写:SEQ)、Acknowledgment Number(确认序列号,简写:AKN)、SYN(标记)、ACK(标记)”等6个字段。我们把这6个字段列出来,感受一下 TCP 是如何创建连接的,如表5-4所示:
表5-4 TCP 连接创建报文(部分字段)
报文 | 源 | 目的 | SEQ | AKN | SYN | ACK |
报文1 | IP = A.ip Port = 1000 | IP = B.ip Port = 2000 | 100 | 0 | 1 | 0 |
报文2 | IP = B.ip Port = 2000 | IP = A.ip Port = 1000 | 1000 | 101 | 1 | 1 |
报文3 | IP = A.ip Port = 1000 | IP = B.ip Port = 2000 | 101 | 1001 | 0 | 1 |
下面我们分别介绍这3个报文:
l 报文1
报文1是从 A(端口号为1000)发往B(端口号为2000),它的标记 SYN = 1(如无特别说明,其他标记都是为0。以下同)。
标记 SYN 是“Synchronize sequence numbers”的简写,直接翻译就是“同步序列号”,背后的含义是:向对方请求建立连接。
当 SYN = 1 的时候,SEQ(Sequence Number)就表示这个序列号是初始序列号(The Initial Sequence Number,ISN)。ISN 有两点需要注意:
(1)ISN 到底该取何值?该如何取值。这个我们放到后面再讲述,这里暂且不管这个问题,就认为报文1的 ISN = 100。
(2)后续报文,都与这个 ISN 相关。这个我们在随后的描述中就可以看到。
l 报文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 = 101。
需要注意的是,不仅仅是连接创建请求时,需要响应 ACK,每一个报文(比如传输数据的报文,下文会介绍)都需要响应,只是当前的例子是响应“连接创建请求”而已。
第2层含义是 B 也对 A 表达了一个“创建连接”的请求,所以我们在报文2中看到:SYN = 1,SEQ = 300。这里的“300”也就是 B 的 ISN。
当 A 收到 B 发过来的报文2时,会心一笑:B 接纳我了,所以我们在图5-16中看到 A 的状态是 ESTABLISHED,意思是:A 自己认为创建了连接。
l 报文3
撒娇是相互的。A 请求 B 抱抱(报文1),B 答应了(报文2)。同时,B 也请求 A 抱抱(报文2),A 也得答应啊。
于是 A 又给 B 回以一个应答报文,就是报文3:从 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 自己认为创建了连接。
A 认为自己创建了连接,B 也认为自己创建了连接,至此 A(端口号1000)与 B(端口号2000)之间的连接创建完成。TCP 称这个创建过程为“三次握手”。如果忽略掉奇数细节,那么所谓“三次握手”其实就是“撒狗粮”。
撒完狗粮以后,我们还得看看技术细节。前文所涉及的几个字段,我们总结一下,如表5-5所示:
表5-5 TCP 几个字段的解释
字段 | 含义 |
SEQ | Sequence Number,32 bits。报文的序列号。TCP 的可靠传输,其实现机制之一,就是借助序列号(下文会讲述)。当 SYN = 1 时,此时的 SEQ 代表 ISN(Initial Sequence Number,初始序列号) |
AKN | Acknowledgment Number,32 bits。必须是 ACK = 1 时,此字段才有意义。AKN 表示期望对方下一个报文的 SEQ = AKN。一般来说,AKN 等于当前所收到报文的 SEQ + 1。 |
SYN | Synchronize sequence numbers,1 bit。字面意思是“同步序列号”,背后的含义是:向对方请求建立连接。 |
ACK | Acknowledgment field significant,1 bit。确认报文的标记。只有 ACK = 1,AKN 才有意义。 |
由于本小节的主题是“创建连接的基本过程”,所以我们忽略了其他字段。笔者以为,看一看实际的报文,对连接的创建能有一个更好的理解,对其他字段也会有个直观的认识。图5-就是“三次握手”报文的抓包图。图的内容有点多(字体也比较小),不过笔者强烈建议您仔细看一看。
图5-17 TCP 连接创建第1次握手报文
图5-18 TCP 连接创建第1次握手报文之 Options
图5-19 TCP 连接创建第2次握手报文
图5-20 TCP 连接创建第2次握手报文之 Options
图5-21 TCP 连接创建第3次握手报文
从图5-17到图5-21,可能看的眼晕,但是仍然值得一看。如果可以的话,您也可以自己抓个包看一看,那样可能更清楚。
需要强调的是,图5-21之后并没有“TCP 连接创建第3次握手报文之 Options”,这是因为第1次握手时已经将 Options 发送到对方,所以第3次握手就没必要再重复了。
5.2.2 一个简单的 TCP 数据传输
TCP 连接的内容还有很多,但是我们必须先跳到 TCP 的数据传输,然后再回过头继续分析 TCP 的连接,否则不太好描述。另外,TCP 数据传输也是非常复杂,这里只是做一个简述,而且这里的简述,也是一个极度简化的描述,甚至还不是很准确——不过这都不是重点,这里的简述只是为了帮助理解 TCP 连接。详细的 TCP 数据传输,我们会放到后面的章节中描述。
在讲述 TCP 数据传输之前,先讲述几个概念:
(1)TCP 传输数据,是以字节为单位,比如1次(1个报文)传输7个字节、53个字节、200个字节、1200个字节等等。所以,TCP 也被称为“基于字节流”的协议(The data that flows on a connection may be thought of as a stream of octets,RFC 793)。
(2)TCP 为每一个字节都做了编号,体现这个编号的就是 TCP 报文头中的序列号(SEQ)字段。
(3)TCP 创建连接请求报文(SYN = 1),也会消耗掉1个编号。
(4)TCP 确认包报文(ACK = 1),不消耗编号。
(5)TCP 确认报文头中的确认序列号(AKN),表达的是期望对方下一个报文头中序列号(SEQ)所取的值。
这5个概念,第1、5两个概念比较好理解,中间3个,则有点让人困惑。
我们还是以图5-16为例,从 TCP 创建连接的“三次握手说起”。为了阅读方便,我们将图5-16重新贴一下,如图5-22所示:
图5-22 TCP 创建连接的基本过程
图5-22中,报文1(第1次握手),SYN = 1,SEQ = 100,此时的 SEQ 就是初始序列号(ISN),后面的故事都将围绕着这个初始序列号展开。
因为 TCP 创建连接请求报文(SYN = 1)会消耗掉1个编号,所以报文2(第2次握手)的 AKN = 101(101 = 100(初始序列号) + 1(SYN 报文消耗掉1个编号)),也就说期望 A 下次发送报文的序列号为 101。
同时报文2自己也是创建连接请求报文,所以它也设置了自己的初始序列号为 300(SEQ = 300,SYN = 1)。
也正是因为报文2自己也是创建连接请求报文,所以它也消耗掉1个序列号。故而,我们看到报文3(第3次握手)的 AKN = 301(301 = 300(初始序列号) + 1(SYN 报文消耗掉1个编号))。
5.2.2.1 TCP 数据传输过程(极简)
我们对初始序列号(ISN)、序列号(SEQ)、确认序列号(AKN)有了一定的认识,那么我们接着图5-22的故事,继续往下走——A(端口号为1000)和B(端口号为2000)建立了 TCP 连接以后,A 向 B 发送数据,如图5-23所示:
图5-23 TCP 数据传输基本过程(简化)
图5-23有1个最大的特点:A 向 B 发送数据,B 收到数据后给 A 回以1个确认报文。所以,我们在图5-23中看到4个报文:报文1是 A 向 B 发送数据,报文2 是 B 向发送确认报文(确认数据已经收到),报文3是 A 向 B 发送数据,报文4 是 B 向发送确认报文(确认数据已经收到)。
这4个报文的标记都是 ACK(ACK = 1,其他标记 = 0)。确认报文的标记是 ACK 比较好理解,发送报文的标记为什么也是 ACK?最简单的解释就是:这种小事不要纠结,TCP 就是这么规定的。
与图5-22一样,图5-23为了表达数据传输的过程,也有意忽略掉很多字段,只保留了“源端口号、目的端口号、Sequence Number(序列号,简写:SEQ)、Acknowledgment Number(确认序列号,简写:AKN)、ACK(标记)”等5个字段。另外图5-22中的 DataLen 表达的是 TCP 数据长度,这个并不是 TCP Header 中的字段,而是计算所得(IP 报文头中的“总长度” - IP 报文头长度 - TCP 报文头长度)。
序列号(SEQ)、确认序列号(AKN)、TCP 数据长度(DataLen)这3个数值非常关键,我们先看看图5-23中的4个报文中这3个字段的值,然后再分析它们为何关键,如表5-6所示:
表5-6 TCP 数据传输报文(部分字段)
报文 | 源 | 目的 | SEQ | AKN | ACK | DataLen |
报文1 | IP = A.ip Port = 1000 | IP = B.ip Port = 2000 | 101 | 1001 | 1 | 100 |
报文2 | IP = B.ip Port = 2000 | IP = A.ip Port = 1000 | 1001 | 201 | 1 | 0 |
报文3 | IP = A.ip Port = 1000 | IP = B.ip Port = 2000 | 201 | 1001 | 1 | 100 |
报文4 | IP = B.ip Port = 2000 | IP = A.ip Port = 1000 | 1001 | 301 | 1 | 0 |
根据前面的描述,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个字节的数据。那么,这100个字节的数据的编号在哪里呢?在心里——这不是开玩笑,因为 TCP 报文中再也没有字段来存储其他字节的编号了。TCP 在心中给其余99个字节所记录的编号是:102、103 ... 200。
在心里记录编号有什么意义呢?我们再看表5-6中的报文2,这是 B 发给 A 的确认报文。报文2的 AKN = 201,这是 B 告诉 A:你下次发报文时,你的 SEQ = 201,也就是你下次报文中的第一个数据字节的编号是201。
如此看来,虽然第1个报文中的99个字节的数据的编号是记录在心里,没有体现在报文里,但是并没有被遗忘。
把 A 发给 B 的传输数据的报文记为“i”,把 B 确认 x 的报文记为“i+1”,把 A 发给 B 的下一个传输数据的报文记为“i+2”,我们就会得到如下的公式:
AKN[i+1] = SEQ[i] + DataLen[i]
SEQ[i+2] = AKN[i+1]
基于这个公式,我们就能很好地理解每个报文的 SEQ、AKN 的取值。
我们再回头看看报文2的 SEQ。因为报文1的 AKN = 1001,也就是说,A 期望 B 的报文(报文2)中的 SEQ = 1001,那么 B 也就随其所愿,设置自己的 SEQ = 1001。
我们继续后面的报文。基于前面的描述,我们会比较清楚地知道,报文3的 SEQ = 201,AKN = 1001(因为报文2仅仅是1个 ACK 报文,并没有传输数据)。同理,我们也会得到报文4 的 SEQ = 1001,AKN = 301。
最后,基于以上的分析,我们再修正一下前面所提出的公式,用代码(伪码)的形式表达(不要在意语法和编程规范,意思到位就行):
if (1 == SYN)
{
syn = 1
}
else
{
syn = 0
}
AKN[i+1] = SEQ[i] + DataLen[i] + syn
SEQ[i+2] = AKN[i+1]
5.2.2.2 TCP 数据传输过程分析(极简)
5.1节曾经提到过:TCP 是一种面向连接的、可靠的、基于字节流的端到端的传输层通信协议。这句话不是我说的,很多资料都是这么说,而且最主要的,官方资料 RFC 793 也是这么说的。
面向连接,这几个字正是本章希望说明的,只是现在还不到时候,我们后面再说。
端到端,是说 TCP 通信的两端分别是某一个主机的某一个进程。
基于字节流,前文已经说了,是因为 TCP 将每个字节都做了编号,也就是说 TCP 传输的数据单元是字节。
可靠,是什么意思呢?TCP 如何做到可靠传输呢?
首先,我们回忆一下上一小节所说的数据传输是从何时开始的?是从 TCP 连接创建好以后开始的!这说明什么?这最起码说明,通信的双方能够“听见”!你说你如果“向天空大声的呼唤说声我爱你”,那有啥用啊,别人也听不见啊!
向天空大声的呼唤说声我爱你
能够创建连接,可以说明两点:通信的两端是“存在的”、通信的网络是连通的(中国电信和中国移动表示不服,^_^)。这是可靠传输最基础的条件,但是还远远不够。
我们再回忆一下上一小节 TCP 给它所传输的每个字节都编号的作法。假设 A 此时的 SEQ = 101,它要给 B 发送 100 个字节,那么 A 是知道这样一个事实:如果 B 回以1个确认报文,并且报文的 AKN = 201,那么 A 知道 B 完全收到了它所发送的100个字节;如果 B 没有回以确认报文,那么 A 知道 B 没有收到它所发送的100个字节。
如果 B 没有收到怎么办?A 就重新发送!如果 B 一直都没收到怎么办(B 一直都没有回以确认报文),A 当然也可以就这样一直发送下去——不过这在通信世界中没有意义,真正有意义的是 TCP 告诉 A:B 可能出问题了,咱放弃治疗,不用再发送了!
以上简短的文字,其实说明了 TCP 可靠传输的最根本的含义:
(1)传输的结果是明确的,要么是成功,要么是失败。TCP 不保证传输绝对成功,因为它不能保证的因素太多,比如传输的过程中,网络断了,TCP 也无能为力,只能传输失败。但是 TCP 能够保证的是传输的结果是明确的,不能说不知道“到底是成功还是失败”
(2)TCP 确定传输是成功的机制就是:(A)给所发送的每个字节都做了编号;(B)通过“发送/确认”报文来明确告知传输成功
(3)如果有暂时的故障致使对方没有收到数据,TCP 通过重传机制来纠错。
TCP 为了保证可靠传输,还有其他很多机制(下文会讲述),但是以上所说的3点,是最根本的。
而要保证这3点,TCP 所基于的就是连接——TCP 连接。但是,TCP 连接到底是什么呢?
5.2.3 TCP 连接是什么
1952年10月19日清晨,美军再次集中火力,轰炸上甘岭阵地。当9连正接听“准备开炮”的指令时,听话器突然没了声,电话线又被炸断了。“我去!”话音未落,金耳世就背着单机,拿着接线钳,带领着战友李昌友向外奔去。
一秒,两秒,半分钟,一分钟……时间滴答过去,战友们在工事里焦急等待着,突然电话里又清晰地传来了指挥部的声音:“向美国强盗开炮!”一发发愤怒的炮弹旋即在敌营炸开了花。
而当战友们找到金耳世他们时,却发现李昌友已经牺牲了,脸上盖着金耳世的帽子,金耳世自己则一手捏着电话线,一手拿着接线钳,昏迷在第三个接线处的地上。他的左腿膝盖处已被炸断,仅剩一根筋连着大腿,鲜血汩汩地往外涌着,他的身后,沿着电话线,是一条绵延20多米长的血路。
没有人知道,他刚才是忍着怎样的剧痛,以怎样的毅力,冒着敌人疾雨般的炮弹,在凹凸不平的满地弹坑与碎石上拖着断腿,爬行了这20多米,直至接好3处断线。战友们含泪将他抬上担架时,还听见他喃喃地说:“刚才……接的……线……都通了吗……”
这成了金耳世的遗言。因为流血过多,他在上甘岭永远地闭上了眼睛。之后,他被授予“特等功臣”称号。
向英雄致敬!
同时英雄也用鲜血告诉我们:TCP 所创建的连接不是物理线路。
战场上拉电话线
抗战中缴获的电话线
当时战场上由于条件限制,很可能没有电话交换机。实际上早在1878年,也就是电话发明两年后,世界上最早的电话交换机出现了。这种交换机,是由话务员进行人工操作的,所以称为“人工交换机”。
话务员和人工交换机
人工电话交换机原理示意图
人工电话交换机
无论是人工交换机还是后来发展的数字程控交换机,打电话的过程与 TCP 连接其实很像,电话通信的过程包括3个阶段:电路建立、保持、拆除。
但是很遗憾,这也不是 TCP 的连接。
不过这也很好理解,毕竟电话交换机属于电路交换,在通话之前,由交换设备在收发双方之间建立一条线路,并在通话期间独占这条线路,直至通话结束。
从复用的角度来讲,电路交换后来又发展到报文交换、分组交换。与电路交换独占一条线路不通,报文交换和分组交换都是采用时分复用的方式以达到线路共享。分组交换的思想来源于报文交换,也可以说是报文交换的升级版。按照实现方式,分组交换可以分为虚电路分组交换和数据报分组交换。
虚电路交换在信息交换之前,需要在发送端和接收端之间先建立一个逻辑连接,然后才开始传送分组,所有分组沿相同的路径进行交换转发,通信结束后再拆除该逻辑连接。如果抽象地看,这是包括3个阶段:逻辑连接的建立、保持和拆除。
但是,同样很遗憾,这也不是 TCP 的连接。
数据报交换与虚电路交换最大的区别就是信息交换之前不需要建立逻辑连接。IP 网络就是典型的数据包交换。所以,IP 网络也称为无连接网络。
既然都称为无连接了,那这更不是 TCP 的连接。
以上我们以电话线、电话交换机举例,更主要的目的是打比方:电话线类比为 TCP/IP 的物理层、电话交换机类别为 TCP/IP 的数据链路层。再加上 IP 本身所代表的网络层,这是想说明,TCP 连接根本不是“物理层、数据链路层、网络层”这3层任何一层的概念。虽然这3层任何一层不通,TCP 连接都建立不起来,但是 TCP 连接本身的概念,确实跟这3层没有关系。
TCP 连接否认三连
TCP 承载于 IP 层,这句话意味:在一个无连接的网络上创建了一个连接!我只能说,如果不是 TCP 连接太深奥,那就是 TCP 连接的命名有问题,给人以太大的困惑!
吐槽没有意义,还得探究本质。笔者以为,造成这种困惑的本质原因是:网络世界的连接的物理存在形式与人类脑海中的形式有着本质的区别。
自从出现了交换以后,无论是电路交换还是报文交换、分组交换,网络世界的连接就没有一个物理的存在,那只是人类脑补的概念,如图5-24所示:
图5-24 连接是人类脑补的概念(1)
图5-24中,网元指的是交换机或者路由器。从5-24可以看到,从 A 到 B,连接好像是存在的,比如:A->网元1->网元2->网元4->B。
但是,如果我们打开网元内部,会发现这些所谓的连接其实都是靠一个“开关”来控制通和断的,如图5-25所示:
图5-25 连接是人类脑补的概念(2)
图5-25中的“开关”是一个比喻,它实际上指的是网元内部的交换模块。可以看到,如果一个网元内“开关”的没有接通其中的两个接口,那么这两个接口就是断的。比如网元1的开关,如果没有接通 I1(Interface1,接口1)、I3,那么 I1 和 I3 就是断的。
极端情况下,所有网元的的“开关”都罢工,那么图5-25中的 A 和 B 是根本通不了的,也就没有连接的存在。
这还不是最主要的,最主要的是:所谓连接,不过是人类脑补的概念,网元中根本就没有这样的概念。
我们假设设置了网元内的开关,从而使“A->I1->I2->I4->I6->I8->I10->B”这一条连接是通的。但是“这一条连接”是人类所理解的概念,对于网元来说,它只知道是按照某种规则拨动了一下开关而已,比如网元1,它只知道拨动了开关,连接了 I1 和 I2,其它的什么都不知道:开关连接了 I1 和 I2,意味着什么?不知道!就是连通了两个接口而已。
人类心中是要建一座教堂,机器的心中不过是搬了一块又一块砖而已。
所以,TCP 连接只是人类的一个想象,在 TCP 模块(协议)里,它一定是一种“原生”的形式存在——无论是什么形式,反正它不是我们想象的那个连接。
RFC 793 并没有明确定义 TCP 连接是什么,反而是各个具体的实现以代码的形式,表达了 TCP 连接是什么(比如 Linux、比如 BSD、比如 windows)。由于不同的实现,其代码不尽相同,所以笔者就不以任何真实的代码为原型,而是以相对抽象的形式来表达。这对您阅读相关的代码可能没有帮助,但是对于概念的理解还是有一定的好处。
说到 TCP 连接,首先就得提到 socket。再强调一遍,笔者给出的是相对抽象的定义,与相关的代码不一样,这一点一定要注意。socket 的定义,如图5-26所示:
图5-26 socket 定义
图5-26,定义了 socket 最根本的两个元素:IP 地址、端口号。那么可以想象,TCP连接首先是一对 socket,如图5-27所示:
图5-27 TCP 连接定义(1)
需要对5-27做一个强调的是,图中的 socket、tcp_connection 都是主机中的数据结构(准确地说,是主机的进程(或者是内核)中的数据结构的实例(对象))。所以我们在图5-27中看到,两个 host 都分别有 tcp_connection 的对象实例。另外,为了画图清晰,图5-27只标识了左边的 tcp_connection 的两个 socket,一个指代自己的 socket,另一个指代对端的 socket。
至此,我们看到了 TCP 连接的部分真相:
(1)每个 host 都会为一个人类所理解的 TCP 连接创建1个数据实例对象(tcp_connection)
(2)这个数据实例对象的值是两个 socket 实例,一个指代自己的 socket,一个指代对端的 socket。
所以,我们在有的资料中看到,它们把 TCP 连接定义为一对 socket(<socket1, socket2>)。
不过这远远不够,RFC 793 说:A pair of sockets uniquely identifies each connection。是的,一对 socket 唯一确定1个 TCP 连接,但是1个 TCP 连接,远远不止一对 socket,它还有更重要的内容,如图5-28所示:
图5-28 TCP 连接定义(2)
图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)。
我们回忆一下“5.2.2.1 TCP 数据传输过程(极简)”中的一句话:TCP 在心中给其余99个字节所记录的编号是:102、103 ... 200。
这里所谓的“心中”,其实就是 TCB 数据实例。当然,TCP 也不会真的给每一个字节都做编号,它只须根据公式计算这次数据传输以后,它下次传输时的序列号应该是什么(然后存储在 TCB 数据实例中),然后与对端发送过来的报文中的确认序列号相比较,如果两者想等,那说明该次传输的数据已经完全被对端完全接收了。
RFC 793 说,TCP 是一个面向连接的协议,从具体实现的角度来讲,它的本质其实是:基于 TCB 数据实例的协议。
世上本没有 TCP 连接,说的人多了,便成了连接。
不过也必须承认,TCP 连接这个名词,虽然给人以误解,但是知道真相以后,再看这个词,还是对 TCP 整体的表达和理解都是有好处,毕竟读起来朗朗上口,听起来形神兼备。
忘记真相,还是记住 TCP 连接这个名词吧。