TCP的连接

TCP 三次握手过程是怎样的?

TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。三次握手的过程如下图:

一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态

客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。

服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1,接着把 SYNACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。

服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。

从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的 ,一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。

如何在 Linux 系统中查看 TCP 状态?

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

为什么是三次握手?不是两次、四次?

相信大家比较常回答的是:"因为三次握手才能保证双方具有接收和发送的能力。"

这回答是没问题,但这回答是片面的,并没有说出主要的原因。

在前面我们知道了什么是 TCP 连接

用于保证可靠性和流量控制的某些状态信息,这些信息包括Socet,序列号,窗口大小。

所以,重要的是为什么三次握手才可以初始化 Socket、序列号和窗口大小并建立 TCP 连接。

接下来,以三个方面分析三次握手的原因:

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 次握手才可以避免资源浪费

*原因一:*阻止重复历史连接的初始化

RFC 793 指出的 TCP 连接使用三次握手的首要原因 :三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。

我们考虑一个场景,客户端先发送了 SYN(seq = 90)报文,然后客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq =100)报文(注意!不是重传 SYN,重传的 SYN 的序列号是一样的)。

客户端连续发送多次 SYN(都是同一个四元组)建立连接的报文,在网络拥堵情况下:

一个「旧 SYN 报文」比「最新的 SYN」 报文早到达了服务端,那么此时服务端就会回一个 SYN +ACK 报文给客户端,此报文中的确认号是 91(90+1)。

客户端收到后,发现自己期望收到的确认号应该是 100 + 1,而不是 90 + 1,于是就会回 RST 报文。

服务端收到 RST 报文后,就会释放连接。

上述中的「旧 SYN 报文」称为历史连接,TCP 使用三次握手建立连接的最主要原因就是防止「历史连接」初始化了连接

原因二:同步双方初始序列号

TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:

  • 接收方可以去除重复的数据;
  • 接收方可以根据数据包的序列号按序接收;
  • 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道);

两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

初始序列号 ISN 是如何随机产生的?

起始 ISN 是基于时钟的,每 4 微秒 + 1,转一圈要 4.55 个小时。

RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, localport, remotehost,remoteport)。

  • M 是一个计时器,这个计时器每隔 4 微秒加 1。
  • F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。

以看到,随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。

既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节;
  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度;

原因

  • 性能开销大:IP 分片需在路由器拆分数据包并添加额外头部,接收端需缓存所有分片并重组,消耗网络和主机资源。
  • 可靠性低:分片丢失会导致整个原始数据包失效,需重传全部数据,而非仅丢失的片段。

TCP 通过三次握手协商 MSS,直接限制数据段大小,从源头避免分片,减少网络负担。,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。

第一次握手丢失了,会发生什么?

当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 SYN_SENT 状态。因为丢失了,所以服务器收不到SYN报文,客户端就无法得到回应,就会一直发送。重传的 SYN 报文的序列号都是一样的

间隔一段时间,发一次。不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,一般是5次

第二次握手丢失了,会发生什么?

客户端发SYN 报文给服务器,服务器回复ACK和SYN报文,但是它丢失了,这就导致客户端收不到回应,就一直发SYN,服务端也不知道未发出去,得不到客户端的ACK,服务端也重发ACK和SYN,所以他们就全部重新发送

第三次握手丢失了,会发生什么?

第三握手丢失,服务器得不到客户端的ACK,因为单独的ACK不会重传,

  • 重传 ACK 无意义(数据重传会自然触发新的 ACK);

当服务端超时重传 2 次 SYN-ACK 报文后,由于 tcp_synack_retries 为 2,已达到最大重传次数,于是再等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到客户端的第三次握手,那么服务端就会断开连接。

什么是 SYN 攻击?如何避免 SYN 攻击?

我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知IP 主机的 ACK 应答,久而久之就会占满服务端的半连接队列,使得服务端不能为正常用户服务。

在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:

  • 半连接队列,也称 SYN 队列;
  • 全连接队列,也称 accept 队列;

正常流程:

1、当服务端接收到客户端的 SYN 报文时,会创建一个半连接的对象,然后将其加入到内核的「 SYN 队列」;

2、接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;

3、服务端接收到 ACK 报文后,从「 SYN 队列」取出一个半连接对象,然后创建一个新的连接对象放入到「 Accept 队列」;

4、应用通过调用 accpet() socket 接口,从「 Accept 队列」取出连接对象。

SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满,这样当 TCP 半连接队列满了,后续再在收到SYN 报文就会丢弃,导致客户端无法和服务端建立连接。

避免 SYN 攻击方式

方式一:调大 netdev_max_backlog

当网卡接收数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。控制该队列的最大值如下参数,默认值是 1000,我们要适当调大该参数的值,比如设置为 10000

方式二:增大 TCP 半连接队列

方式三:加cookie身份验证

方式四:减少 SYN+ACK 重传次数

相关推荐
FreeBuf_38 分钟前
黄金旋律IAB组织利用暴露的ASP.NET机器密钥实施未授权访问
网络·后端·asp.net
Tanecious.2 小时前
C++--红黑树封装实现set和map
网络·c++
阿维的博客日记3 小时前
TCP和UDP区别
tcp/ip·udp·php
yqcoder3 小时前
7. TCP 和 UDP 的区别
网络·网络协议·http
wanhengidc3 小时前
UDP服务器的优缺点都包含哪些?
服务器·网络协议·udp
weixin_456732593 小时前
tcpdump交叉编译
网络·测试工具·tcpdump
诗人不说梦^5 小时前
[BUUCTF 2018]Online Tool
linux·运维·服务器
IT WorryFree5 小时前
macos安装iper3
网络·macos·iperf·打流
hrrrrb5 小时前
【TCP/IP】14. 远程登录协议
网络·网络协议·tcp/ip
wa的一声哭了5 小时前
python基础知识pip配置pip.conf文件
java·服务器·开发语言·python·pip·risc-v·os