TCP 的基本认识
TCP 中文又被称之为传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。这个特性的解释如下:
- 面向连接的:面向连接也就是说
一对一
才能连接。 - 可靠的:无论网络中出现了什么变化,TCP都能保证一个报文一定能够到达接收端
- 字节流:消息是
没有边界的
,所以无论消息有多大都可以进行传输
学习过网络的朋友就知道,对于一个协议来说,往往会分成多个层,层与层之间解耦,对于 TCP 传输控制层协议来讲,它也是这样的,下面是 TCP 协议的一个结构示意图:
image-20210702194138037
可以看到,TCP 的应用层就对应着 OSI
参考模型的应用层、表示层、会话层,传输层和网络层是一一对应的,然后紧接着是网络接口层,网络接口层对应着 OSI
参考模型的 数据链路层、物理层。在说了 TCP/IP
的分层模型,紧接着来介绍下 TCP 的头部格式,它的头部格式如下所示:
image-20210702200835386
上图就是针对于 TCP 头部的一个示意图,下面对于几个概念进行解释:
- 序列号:用来解决网络的乱序问题
- 确认应答号:指下一次期望收到的数据的序列号,用来解决不丢包的问题
- 控制位:
- ACK: 该位为 1 时,
确认应答
的字段变为有效,TCP规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 - RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接
- SYN: 该位为 1 时,表示期望建立连接,并在其
序列号
的字段进行序列号初始值的设定 - FIN :该位为 1 时,表示今后不再有数据发送,希望断开连接。
那对于 TCP 来说,什么叫做连接呢,什么样子才说是两台设备已经连接起来了呢?简单来说,也就是用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称之为连接。那么 TCP 建立一个连接,那么就需要客户端和服务器端达成一个共识,共识的内容就包括:
- Socket:由
IP
地址和端口号组成 - 序列号:用来解决乱序问题
- 窗口大小:用来做流量控制
那么又该如何确定一个连接呢?就是说是哪台设备中的哪个进程与哪台设备中哪个进程进行通信,需要如下几个信息:
- 源地址
- 源端口号
- 目标地址
- 目标端口号
TCP 的运输连接
在前文中叙述了 TCP 是面向连接的协议,它是基于传输连接来传送 TCP 报文段,TCP 传输连接的建立和释放是每一次面向连接的通信中必不可少的过程,TCP 的运输连接主要有以下三个阶段:
- 建立 TCP 连接
- 数据传送
- 释放 TCP 连接
image-20210702212305512
上述就是 TCP 运输连接的一个基本过程,基于此,我们来依次对这几种过程进行剖析。
TCP 建立连接
TCP 建立连接要解决的是以下三个问题:
- 使得 TCP 双方能够确知对方的存在;
- 使得 TCP 双方能够协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等);
- 使得 TCP 双方能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配
那么建立连接的过程主要如下图所示:
image-20210703134143289
根据上图所示,我们可以看到 TCP 建立连接时的一个基本流程:
首先:TCP服务器进程首先创建传输控制块,用来传输 TCP 连接中的一些重要信息:TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前发送和接收序号等等。。。之后呢,准备接受TCP客户进程的连接请求,此时,TCP服务进程就进入监听状态,等待 TCP 进程的连接请求,需要注意的是作为 TCP 服务端,这里是被动打开连接。
其次:,对应着第一个箭头,SYN=1,seq=x
。TCP 客户端进程首先创建传输控制块,然后,在打算建立连接时,向TCP服务器进程发送TCP连接请求报文段,并进入同步已经发送状态。TCP连接请求报文段首部中的同步位SYN被设置为1,也就表明这是一个 TCP 连接请求报文段,序号字段 seq 被设置了一个初始值,作为 TCP 客户进程所选择的初始序号,(这里要注意的是:TCP规定 SYN 设置为 1 的报文段不能够携带数据 ,但是要消耗掉一个序号,由于 TCP 连接建立是由 TCP 客户端主动发起的,因此称之为主动打开连接)
紧接着:,对应着第二个箭头,SYN=1 ACK=1 seq=y ack=x 1
。TCP 服务端如果同意客户端建立连接,则向 TCP 客户端进程发送 TCP 连接请求确认报文段,并进入同步已经接收状态
最后:,TCP 客户端收到连接请求确认报文段之后,还需要向 TCP 服务进程发送一个普通的 TCP 确认报文段,并进入连接已建立状态。该报文是 ACK=1 seq=x 1 ack=y 1
,也就表明这是一个普通的 TCP 确认报文段。(普通的 TCP 确认报文段可以携带数据,但是如果说当前的 TCP 确认报文段不携带数据,那么也就不消耗数据报文段的序号,也就是说下一个数据报文段的序号还是 x 1),当 TCP 服务端收到这个普通认报文段之后,TCP服务端也进入连接已建立状态,这时 TCP 客户端和 TCP 服务端都进入了连接已经建立状态,就可以开始传输数据了。
上述就是一个三次报文握手建立连接的一个过程,那么分析到这里,就出现了一个问题,就是说当 TCP 服务端发送连接请求确认报文段之后,当 TCP 客户端收到这个报文,难么就进入连接已建立状态了,这个时候直接发送数据不就行了,为什么还要再次发送一个 TCP 普通确认报文段呢?这是为什么?
要解释这个问题,那首先假设当前 TCP 建立连接采用的是两报文握手连接,那么在 TCP 客户端发送连接请求报文段之后,TCP 服务器接收到连接请求报文段就进入连接已经建立状态,进一步 TCP 服务端发送连接请求确认报文段,这个时候,TCP 客户端收到报文之后,就也进入连接已经建立状态。然后,基于此,我们来看下面这样一个情况:
image-20210703142025909
如上图所示,由于 TCP 客户端发送的第一个连接请求报文段没有能够成功发送出去,TCP 服务端没有能够接收到这个报文段,这个时候,TCP客户端又重新发起了一个连接请求报文段,依据刚刚所说的过程,TCP 服务器在接收到这个报文段之后,就进入了连接已经建立状态,紧接着,客户端也进入了连接已经建立状态,就可以进行数据传输了,但是,这个时候,由于之前的 TCP 连接请求报文发送失败,导致的超时重传,在TCP已经释放连接的时候,TCP 客户端收到连接请求报文段,进入连接已经建立状态,但是这个时候对于 TCP 客户端来说,它已经关闭连接了,无法进入连接已经建立状态,这样也就会导致 TCP 服务器白白的在干等,在浪费资源。
这也就是为什么采用三次报文握手,而不采用两次报文握手的原因。
TCP 释放连接
数据传输完毕之后,TCP双方都可以释放连接,现在 TCP 客户进程和 TCP 服务进程都处于连接已建立状态,如果使用 TCP 客户进程中的应用进程通知其主动关闭 TCP 连接,TCP 客户进程会发送 TCP 连接释放报文段,并进入终止等待1状态。发送的报文称之为是 TCP 释放报文段,这里需要注意的是 FIN == 1 的报文段即使不携带数据,也要消耗掉一个序号。
image-20210703143711751
当 TCP 服务端收到来自 TCP 客户端的释放报文段的时候,会发送一个普通的 TCP 确认报文段并进入关闭等待状态,具体过程如下图所示:
image-20210703144123880
这个时候,TCP 服务段进程要通知高层应用进程TCP客户端要断开与自己的连接,这个时候 TCP 客户端的连接已经断开了,也就是 TCP 客户进程已经没有数据要发送了,但是TCP 服务器进程如果还有数据要发送,TCP 客户进程仍然要接收,那么也就是说从 TCP 服务进程到 TCP 客户进程这个反向的连接还没有关闭,这个状态可能会持续一段时间,当前的 TCP 连接处于半断开连接。
image-20210703144900631
当 TCP 客户端进程收到来自TCP服务端进程的确认报文段之后,就进入终止等待 2 状态,等待 TCP 服务器进程发出的 TCP 连接释放报文段,如果处在 TCP 服务器中的应用进程已经没有数据要发送了,那么应用进程就通知 TCP 服务器进程释放连接,TCP 服务器进程发送 TCP 连接释放报文段并进入最后确认状态
image-20210703145505399
在 TCP 客户端进程接收到 TCP 服务端进程的连接释放报文段之后,必须针对该报文段发送普通的 TCP 确认报文段,之后进入时间等待状态:
image-20210703145711017
在 TCP 服务器进程收到TCP客户端进程发送过来的普通TCP确认报文段之后,就进入关闭状态,而对于 TCP 客户端来说,需要经过 2MSL 之后才关闭连接。
image-20210703151302183
其中 MSL 为最长报文寿命。那说到这里,又出现一个问题,也就是说当 TCP 客户端发送 TCP 普通确认报文段之后,为什么不直接就进入关闭状态,而是还需要等待 2MSL 呢,有这个必要么,为了解决这个问题,我们来看如下的解释,比如说当前就不需要进行等待了,具体通信过程如下图所示:
image-20210703151819583
就是说,当 TCP 服务器进程往 TCP 客户端进程发送释放连接报文段的时候,在 TCP 客户端接受到这个报文,转而 TCP 客户端发送普通确认报文,但是这个报文就丢失了,而此时 TCP 客户端进程已经断开连接,无法再次发送普通确认报文,TCP 服务端进程没有收到普通确认报文,那么就无法进入关闭状态,就会抑制超时重传 TCP 连接释放报文,出现问题。
综上所述,也就是说 TCP 客户端等待 2MSL 的时间是为了能够使得 TCP 服务器端进程能够收到最后一个 TCP 确认报文段而进入关闭状态 。对于另外一个原因,等待 2MSL 才关闭也能够使得本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使得笑一个 TCP 连接中,不会出现旧连接的报文段。以上就是 TCP 通过“四报文挥手”释放连接的过程。
小结
上述就是关于 TCP 运输连接的一个基本内容阐述,详细论述了 TCP 三次报文挥手建立连接以及四次报文挥手释放连接的详细过程,当然 TCP 还有其他内容,剩下的下一期再进行阐述吧~