TCP:三次握手

TCP 三次握手

 

前言

网络层可以实现两个主机之间的通信。真正进行通信的实体是主机中的进程,是一个主机中的一个进程与另外一个主机中的一个进程在交换数据。

IP 协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才应该是应用进程之间的通信。

TCP(Transmission Control Protocol 传输控制协议)

  • 提供面向连接的、可靠的、面向字节流的数据传输服务
  • 通过 TCP 连接传送的数据,无差错、不丢失,不重复,按序到达
  • 每一条 TCP 连接只能是点到点的
  • 在传送数据前必须先建立连接,数据传送完成后要释放连接。因此 TCP 是一种可靠的运输服务,但是正因为这样,不可避免的增加了许多的开销,比如确认,流量控制等
  • 对应的应用层的协议主要有 SMTP,TELNET,HTTP,FTP 等

UDP (User Datagram Protocol 用户数据报协议)

  • 提供非面向连接的(面向事务)、不可靠的数据、面向报文的传输服务
  • UDP 尽最大努力交付,即不保证可靠交付
  • UDP 支持一对一、一对多、多对一和多对多的交互通信
  • 在传送数据前不需要先建立连接,远地的主机在收到 UDP 报文后也不需要给出任何确认。虽然 UDP 不提供可靠交付,但是正是因为这样,省去和很多的开销,使得它的速度比较快,比如一些对实时性要求较高的服务,就常常使用的是 UDP
  • 对应的应用层的协议主要有 DNS,TFTP,DHCP,SNMP,NFS 等

 

常用端口号

TCP 概述

TCP 把连接作为最基本的对象,每一条 TCP 连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到 IP 地址即构成了套接字,

例如,若 IP 地址为 192.3.4.16 而端口号为 80,那么得到的套接字为 192.3.4.16:80

 

TCP connection

客户端与服务器之间数据的发送和返回的过程当中需要创建一个叫 TCP connection 的东西

由于 TCP 不存在连接的概念,只存在请求和响应,请求和响应都是数据包,它们之间都是经过由 TCP 创建的一个从客户端发起,服务器接收的类似连接的通道,这个连接可以一直保持,http 请求是在这个连接的基础上发送的

在一个 TCP 连接上是可以发送多个 http 请求的,不同的版本这个模式不一样

  • 在 HTTP/1.0 中这个 TCP 连接是在 http 请求创建的时候同步创建的,http 请求发送到服务器端,服务器端响应了之后,这个 TCP 连接就关闭了
  • HTTP/1.1 中可以以某种方式声明这个连接一直保持,一个请求传输完之后,另一个请求可以接着传输。 这样的好处是:在创建一个 TCP 连接的过程中需要“三次握手”的消耗,“三次握手”代表有三次网络传输。 如果 TCP 连接保持,第二个请求发送就没有这“三次握手”的消耗
  • HTTP/2 中同一个 TCP 连接里还可以并发地传输 http 请求

 

TCP报文格式

源端口和目的端口

  • 各占 2 个字节,分别写入源端口和目的端口

序号(sequence number)

  • seq 序号,占 4 个字节,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记
  • TCP 连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 301 ,而携带的数据共有 100 字段,显然下一个报文段(如果还有的话)的数据序号应该从 401 开始

确认号(acknowledgement number)

  • ack 序号,占 4 个字节,只有 ACK 标志位为1时,确认序号字段才有效,ack=seq+1
  • 是期望收到对方下一个报文的第一个数据字节的序号。例如,B 收到了 A 发送过来的报文,其序列号字段是 501,而数据长度是 200 字节,这表明 B 正确的收到了 A 发送的到序号 700 为止的数据。因此,B 期望收到 A 的下一个数据序号是 701,于是 B 在发送给 A 的确认报文段中把确认号置为 701

标志位(Flags)

  • 共 6 个,即 URG、ACK、PSH、RST、SYN、FIN 等
  • 紧急 URG,当 URG=1,表明紧急指针(urgent pointer)字段有效。告诉系统此报文段中有紧急数据
  • 确认 ACK,仅当 ACK=1 时,确认号字段才有效。TCP 规定在连接建立后所有报文的传输都必须把 ACK 置 1
  • 推送 PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将 PSH=1
  • 复位 RST,当 RST=1,表明 TCP 连接中出现严重差错,必须释放连接,然后再重新建立连接
  • 同步 SYN,发起一个新连接,在连接建立时用来同步序号。当 SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使 SYN=1,ACK=1
  • 终止 FIN,用来释放连接。当 FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放

数据偏移

  • 占 4 位,它指出 TCP 报文的数据距离 TCP 报文段的起始处有多远 

保留

  • 占 6 位,保留今后使用,但目前应都为 0

窗口

  • 占 2 字节,指的是通知接收方,发送本报文你需要有多大的空间来接受

检验和

  • 占 2 字节,校验首部和数据这两部分

紧急指针

  • 占2字节,指出本报文段中的紧急数据的字节数

选项

  • 长度可变,定义一些其他的可选的参数

注意事项

  • 不要将确认序号 ack 与标志位中的 ACK 弄混了
  • 确认方 ack= 发起方 seq+1,两端配对

 

TCP 连接的建立(三次握手,Three-Way Handshake)

最开始的时候客户端和服务器都是处于 CLOSED 状态。主动打开连接的为客户端,被动打开连接的是服务器。

TCP 服务器进程先创建传输控制块 TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了 LISTEN(监听)状态;TCP 客户进程也是先创建传输控制块 TCB,进入 LISTEN(监听)状态。

随后开始“三次握手”

第一次:首先客户端向服务器端发送一段TCP报文

  • 标记位为 SYN,表示“请求建立新连接”;
  • 序号为 seq=X(X一般为1);
  • 随后客户端进程进入了 SYN-SENT(同步已发送状态)状态
  • TCP 规定,SYN 报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。

第二次:服务器端接收到来自客户端的 TCP 报文之后,结束 LISTEN 阶段。如果同意连接,则发出确认报文

  • 标志位为 SYN 和 ACK,表示“确认客户端的报文 seq 序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);
  • 确认报文中应该 SYN=1,ACK=1,确认号是 ack=x+1,同时也要为自己初始化一个序列号 seq=y
  • 确认号为 ack=x+1,表示收到客户端的序号 seq 并将其值加 1 作为自己确认号 ack 的值;随后服务器端进入 SYN-RCVD(同步收到)阶段。
  • 这个报文也不能携带数据,但是同样要消耗一个序号。

第三次:客户端接收到来自服务器端的确认收到数据的 TCP 报文之后,明确了从客户端到服务器的数据传输是正常的,结束 SYN-SENT 阶段。并返回最后一段 TCP 报文

  • 标志位为 ACK=1,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);
  • 序号为 seq=x+1,表示收到服务器端的确认号 ack,并将其值作为自己的序号值;
  • 确认号为 ack=y+1,表示收到服务器端序号 seq,并将其值加1作为自己的确认号 ack的值;
  • 随后客户端进入 ESTABLISHED(已建立连接)阶段。
  • TCP 规定,ACK 报文段可以携带数据,但是如果不携带数据则不消耗序号。

后续

  • 服务器收到来自客户端的“确认收到服务器数据”的 TCP 报文之后,明确了从服务器到客户端的数据传输是正常的。结束 SYN-SENT 阶段,进入 ESTABLISHED 阶段。
  • 在客户端与服务器端传输的 TCP 报文中,双方的确认号 ack 和序号 seq 的值,都是在彼此 ack 和 seq 值的基础上进行计算的,这样做保证了 TCP 报文传输的连贯性。一旦出现某一方发出的 TCP 报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
  • 此后客户端和服务器端进行正常的数据传输。

 

“三次握手”的动态过程

 

为什么 TCP 客户端最后还要发送一次确认(第三次握手)?

一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

这样没有给服务器端一个创建还是关闭连接端口的请求,服务器端的端口就一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。那么服务器端上没有接收到请求数据的上一个端口就一直开着,长此以往,这样的端口多了,就会造成服务器端开销的严重浪费。

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

参考:https://blog.csdn.net/qzcsu/article/details/72861891

参考:https://www.cnblogs.com/AhuntSun-blog/p/12028636.html

原文地址:https://www.cnblogs.com/dc2019/p/13818114.html