前言
一个常见的题目,说说 TCP 的三次握手,我们先来交代一下 IP 协议和 TCP 协议,我们都知道IP 协议是无连接的通信协议,它不会占用两个正在通信的计算机之间的通信线路。这样 IP 就降低了对网络线路的需求,每条线可以同时满足许多不同的计算机之间的通信需要,通过 IP 消息或者其他数据呢,会被分割为较小的,独立的包。并通过因特网在计算机之间传送 IP,负责将每个包路由至它的目的地。但 IP 协议呢,没有做任何事情来确认数据包是否按顺序发送,或者包是否被破坏。所以 IP 数据包呢,是不可靠的,需要由它的上层协议来做出控制,前面我们了解到传输控制协议 TCP 是属于传输层的协议。那传输控制协议呢?它的英文名是transmission control protocol
缩写,就是我们的 TCP啦。
传输控制协议TCP简介
- 是一种面向连接的,可靠的,基于字节流的传输层通信协议。
- 数据传输时将应用层的数据流,分割成适当程度的报文段并发送给目标节点的TCP层
- 数据包都有序号,对方收到则发送ACK确认,未收到则重传
- 使用校验和来检验数据在传输过程中是否有误
报文段的长度呢?通常受该计算机连接的网络的数据,链路层的最大传输单元及 mpu 的限制。之后TCP 把结果包传给 IP 层,由它来通过网络将包传送给目标节点的 TCP 层。TCP 为了保证不丢失包。就给每个包一个序号及 sequence number
,同时呢,序号也保证了传送到目标节点的包的按需处理,然后接收端的实体对已成功收到的包呢,发回一个相应的确认及 ack
确认。如果发送端实体在合理的往返时延即 rtt
内未收到确认,那么对应的数据包就会被假设为已丢失,并且将会对其进行重传。TCP 用一个奇偶校验和函数呢,来检验数据是否有错误,在发送和接收时呢,都要计算校验。
TCP报文头
咱们再来学习一下 TCP 报文的头部。如图所示,其中 source port
和 destination port
分别表示原端口和目的端口,它们各占两个字节, TCP 和后面要学习的 udp 的数据包呢,都是不包含 IP 地址信息的。因为那是 IP 层上的事,但是 TCP 和 udp 均会有原端口和目的端口,就是说端口这东西是属于传输层知识范畴的,我们知道两个进程在计算机内部进行通信,可以由管道内存共享。信号量消息队列等方法进行通信的,而两个进程如果需要进行通讯,最基本的一个前提是能够唯一的标识一个进程,通过这个唯一标识呢,找到对应的进程,在本地进程通讯中呢,我们可以使用 PID及我们的这个进程标识符,就是我们进程号呢来唯一标识一个进程,但 PID 只在本地唯一,如果把两个进程放到了不同的两台计算机,然后他们要进行通信的话呢。PID 就不够用了,这样就需要另外一个手段了解决这个问题的方法就是在传输层中使用协议端口号及我们的protocol port number
简称端口,我们知道 IP 层的 IP 地址呢,可以唯一标识主机。而 TCP 协议和端口号呢,可以唯一标识主机中的一个进程,这样我们可以利用 IP 地址加协议加端口号,这么一个唯一标识呢,去标识网络中的一个进程。在一些场合,也把这种唯一标识的模式呢,称为套接字及 socket。也就是说,虽然通信的重点是应用进程,但我们只要把要传送的报文交到目的,主机的某一个合适的端口。剩下的工作就由 TCP 来完成了,咱们再来看看比较重要的 sequence number
就是我们的 seq
序号,它呢是占了四个字节 TCP 连接中传送的字节流中的每个字节呢。都按顺序去编号了,例如一段报文的序号字段值就是 107,而携带的数据呢,共有 100 个字段,那么如果有下一个报文段的话呢,其序号呢,就应该是从 107 100=207 开始。接下来也是比较重要的域,就是我们的这个 ack 确认号 acknowledgement number
,同样的是这样四个字节是期望收到对方下一个报文的第一个数据字节的序号。
例如 b 收到了 a 发送过来的报文,其序列号字段是 301,而数据长度是 200 字节,这表明了 b 正确的收到了 a 发送的到序号 500 为止的数据啊 301 200-1 就是 500 为止的数据。因此 b 希望收到 a 的下一个数据序号呢?是 501 于是 b 在发送给 a 的确认到文段中会把 ack 确认号呢?
置为 501。接下来就是我们的这个 offset 及数据偏移,由于头部有可选字段,长度不固定,因此它指出 TCP 报文的数据距离 TCP 报文的起始处有多远。就是我们的这个 offset 了,那接下来呢,这个 reserve 呢,是保留域保留今后使用的,但目前呢,都会被标为零,接下来这一个就是我们的 TCP flags
。及控制位主要由八个标志位来组成,每一个标志位表示一个控制功能。
咱们主要介绍一下 TCP flags
中的常见的六个,分别是下面这几个,第一个呢是 URG
紧急指针标志。当它为1时呢,表示紧急指针有效为零,则忽略紧急指针。第二个呢,是 ACK
及确认序号标志为1时呢,表示确认号有效为零,表示报文中不含确认信息。忽略确认号字段上面的确认号是否有效,就是通过该标识位控制的。第三个是 PSH
psh唯一标志,表示是带有 push 标志的数据。只是,接收方在接收到该报文段以后呢?应尽快将这个报文段交给应用程序,而不是在缓冲区排队。第四个是 RST
即 reset,就是我们的重置连接标志。用于重置,由于主机崩溃或其他原因而出现错误的,连接或者用于拒绝非法的报文段和拒绝连接请求。第五个就是 SYN
及我们的 synnumber
同步序列号,用于建立连接过程,在连接请求中 syn=1 和 ack=0 表示,该数据段没有使用捎带的确认域。而连接应答,稍带一个确认呢?即 syn=1 和 ack=1。 第六个呢?是 FIN
及 finish 标志用于释放连接,唯一时表示发送方已经没有数据发送了。即关闭本方数据流。
接下来呢,是 window 窗口,指的是滑动窗口的大小,用来告知发送端,接收端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。再往下呢,就是 checksum 简验核了,指的是基友校验,此校验核是对整个的 tcp 报文段,包括 TCP 头部和 TCP 数据,以 16 位进行计算所得。由发送端计算和存储,并由接收端进行验证,接下来是 urgent pointer 及紧急自身,只有当 TCP flags 中的 uig 唯一的时候呢,才有效指出本报文段中的紧急数据的自结束。最后一个呢是 TCP options 可选项,其长度可变定义一些,其他的可选参数当应用程序,希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址,在双方握手之后 TCP 将在两个应用之间建立一个全双工的及服务 duplex 的通信。这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。
而上面说的握手及咱们所谓的 TCP 的三次握手。当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求,这个请求必须被发送到一个确切的地址。在双方握手之后呢,TCP 将在两个应用程序之间建立一个全双工的通信,这个全双工的通信将占用两个计算机之间的通信线路,直到它被方或双方关闭为止。
那什么是全双工呢?也就说我计算机 a 可以给 b 去发送信息,在发送信息的同时,b 也能够给 a 回发信息,那我们上面说的握手呢,就是我们所谓的 TCP 的三次握手了。三次握手的流程图如下,
最开始的时候,我们假设 a 和 b 呢,首次进行通信,那一开始的时候,客户端和服务器呢,他们都会处于 close 的状态。那我们假设这里主动打开连接的是客户端,被动打开连接的是服务端,刚开始的时候呢 TCP 服务器进程呢,先创建传输控制块 tcb,时刻准备接受其他客户进程发送过来的连接请求。此时服务端进入了 listen 及监听的状态,那此时我们的 TCP 客户端进程也是先创建一个传输控制块 TCP。然后向服务器呢发出连接请求报文,这就是我们报文头里的 TCP flags 中的同步序号 syn
,它等于一同时选择一个初始序号 sequence=x,这个 x 呢可以是任意的一个正整数值。此时 TCP 客户端进程呢,就进入了一个 syn-sent 的,这么一个同步已发送的状态,此时发送过去的这一个数据包呢及报文段呢,会被称为 syn 报文段,它是不能携带数据的。但是要消耗掉一个序号,这便是第一次握手了。
那当我们的服务器接收到请求报文之后,如果同意连接,则发出确认报文,确认报文中包含了 TCP flags 中的两个位的字段,一个是 ack。它等于一另外的一个是 syn ,也是等于一那它的确认号,就是 x 1。 那这里的 ack 为什么等于 x 1 呢?原因是因为我们之前呢,在syn 文里面呢,指定了 sixnumber=x。那作为回应呢,他要回应跟 x 相关的信息,并且由于上面的一个报文消耗掉了一个序号,因此它就成为了 x 1,同时也要为自己的这个缓存初始化一个序列号及 sequence=y。 此时服务器就进入到了 s receive 及同步收到的这个状态,这个报文也是不能携带数据的,并且同样需要消耗一个序号,这便是第二次握手。
那么当 tcp 客户进程收到确认报文之后,还要向服务器给出一个确认确认报文的 ack=1。此时这个小的 ack 等于 y 1 原因,是因为刚刚你服务器给我发了一个 sequencenumber=y 嘛,我作为回应 acknowledge,同时这个报文也会消耗掉一个sequence,因此我回应给你的就是 y 1。同时由于我先前你告知我,我的这个序号已经被加一了,因此呢我这里就乖乖的给加上一。equence=x 1 此时 TCP 连接建立客户端呢,就进入了 establish 及已建立连接的状态。TCP 规定呢这个 ack 报文段可以携带数据,前两个都是不可以携带的,当然它也可以不携带。如果不携带数据呢,就不会消耗序号,这便是第三次握手了。那当服务器收到客户端的确认后。也会进入到 establish 的状态,此后双方就可以开始通信了