TCP(二) -- 三次握手

一:摘要概述

系列第一文TCP(一) -- 初识TCP中描述了TCP是一个面向连接的传输层协议,这也是TCP协议保证可靠性的重要一环。客户端与服务端建立连接的方式就是通过三次握手,三次握手的过程中将会交换大量数据信息。本文的目的就是详细解释TCP三次握手的过程、状态变更以及交换的初始数据信息。再次重申文章是阅读张师傅的小册总结,感兴趣的朋友可以购买,真的物超所值

二:协议标志

在这里插入图片描述
第一篇文章中提到TCP协议头有一个Flags标志,标志中的信息代表了数据包的类型。三次握手的过程中就会使用到SYN、ACK包,具体常用的标志含义如下表所示:

序号 标签种类 标签含义 备注
1 SYN 请求连接数据包标志 三次握手时
2 ACK 确认数据包 数据接收方收到数据包后确认通信的数据包
3 FIN 断开数据包 四次挥手时传递
4 RST 强制断开 某些不合法操作时强制断开连接返回数据包
5 PSH 传输层别缓存,立即将数据交给应用层

三:连接过程

在这里插入图片描述

    1. 客户端发送SYN包到服务端,Hello兄弟我想创建一个连接
    1. 服务端收到客户端SYN包回复SYN+ACK,兄弟我收到连接请求可以连接
    1. 客户端回复服务端ACK包,好的,连接创建成功接下来将发送数据

四:状态变更

如第三节示例图所示:

  1. 服务端启动应用监听某个端口时就会处于LISTEN状态
  2. 客户端发起连接发送SYN包客户端处于SYN-SENT状态
  3. 服务端收到SYN包回复SYN+ACK后处于SYN-RECV状态
  4. 客户端收到SYN+ACK回复ACK后处于ESTAB状态
  5. 服务端收到ACK包后处于ESTAB状态

五: Sequence Number

连接建立的过程中可以看到有0、1信息的交换,这个信息到底是什么?有什么作用?

5.1 作用详解

TCP协议是一个可靠的协议,如果数据包丢了那么会进行重传尝试,但是数据包有很多,发送方怎么确认哪一段数据包丢失?依赖的就是Sequence Number,可以理解为该属性就是数据包的编码,方便TCP协议管理数据

5.2 初始值定义

很多资料上亦或是本文上述绘制的过程图中都将Sequence Number的初始值定义为0,现实中的该初始值真为0么?必然这个概念是错误的,也就是为了方便描述计数,所以将其初始值定义0。WireShark抓包工具中显示0是因为其自动帮我们做了处理,可以通过设置:Edit(编辑E) -- Preferences(首选项P) -- Protocols -- TCP恢复原始值显示

在这里插入图片描述

实际上Sequence Number的初始值是一个随时间递增的数值,有着自己的生成算法。使用tcpdump抓包显示结果如下:

在这里插入图片描述

5.3 固定初始值

这时候就会有人问为什么Sequence Numer的初始值不能设置为一个固定值?有以下两个原因值得深思:

  • TCP连接四元组件:src host/port + dst host/port,这些连接信息十分容易获取,如果固定初始值就使得第三方十分容易构造一个在窗口允许范围内的RST数据包关闭连接,这样导致的后果可想而知
  • 后面会讲到SO_REUSEADDR参数,端口复用的情况下如果Sequence Number固定值开始,那么有可能造成新旧连接的数据包一致,就会导致服务端无法判断这个数据包到底是什么时候的数据包

六:ACK确认码

三次握手创建连接时候发现每次对方发送SYN包后都需要ACK进行确认,不仅如此,后续包括数据传输、四次挥手过程都需要ACK确认,这样才能保证数据接收方收到了传输的数据。ACK确认的值到底是多少?其实也比较简单,ACK 数值 = Sequence Number + 数据包大小,表示这个范围内的数据已经接收到,下次传递数据的时候请使用ACK数值作为你的Sequence Numer传递

七:三次握手重试

三次握手的过程其实就是客户端与服务端三次数据包传递交互的过程,既然涉及到数据包的传递那么就有可能因为网络波动等原因导致的数据包丢失。这时候发送数据包的一方就会根据情况尝试重试

7.1 SYN重试

天有不测风云,人有祸兮旦福。网络波动导致客户端发送的SYN包服务端未收到从而没有回复SYN+ACK,那么这时候客户端会如何处理?明显客户端会进行重试,也就是当客户端在一定时间内未收到服务端的ACK确认那么就会重新发送SYN包,示例如下

在这里插入图片描述
重试多少次?这个数值由服务器的 tcp_syn_retries决定,查看该数值命令如下:

[root@bogon ~]# cat /proc/sys/net/ipv4/tcp_syn_retries 
复制代码

packetdrill构建SYN_SENT状态连接使用如下脚本:

+0   socket(..., SOCK_STREAM, IPPROTO_TCP) = 3

+0 connect(3, ..., ...) = -1
复制代码

7.2 SYN + ACK重试

服务端接收到客户端传送的SYN包之后会回复SYN+ACK,这时候如果该数据包丢失的话客户端不会回复ACK,自然当时限达到的情况下服务端需要重新发送SYN + ACK。重试的次数由参数tcp_synack_retries空值,查看该数值命令如下:

[root@bogon ~]# cat /proc/sys/net/ipv4/tcp_synack_retries
复制代码

packetdrill构建SYN_RECV状态连接使用如下脚本:

+0  < S 0:0(0) win 65535  <mss 100>
+0  > S. 0:0(0) ack 1 <...>
复制代码

猜你喜欢

转载自juejin.im/post/5e000f9b5188251284219373