diva-notes
  • README
  • Ads
    • 定价策略
    • 广告层级
    • 归因模型
    • 买量
    • Chat GPT
    • Google
  • AI
    • 参考资料
    • Chat GPT
    • stable-diffusion-webui安装
  • Algorithm
    • 倍增
    • 并查集
    • 参考
    • 环的判断
    • 凸包
    • 蓄水池抽样
    • 最短路径
    • 最小生成树
    • KMP算法
    • Rabin-Karp算法
    • Tarjan桥算法
  • Architecture
    • Serverless
  • Career
  • CICD
    • 代码质量
    • CICD实践
  • Data Structure
    • 布谷鸟过滤器
    • 布隆过滤器
    • 浮点
    • 红黑树
    • 锁
    • LSM树
  • DB
    • My SQL
      • 隔离级别
      • 架构
      • 索引
      • 锁
      • 页结构
      • 主从同步
      • ACID
      • Log
      • MVCC
      • Questions
    • Postgres
      • 持久化
      • 对比MySQL
      • 隔离级别
      • 索引
      • Greenpulm
      • MVCC
    • 倒排索引
    • 列式存储
    • H Base
    • HDFS
    • MPP数据库选型
    • Questions
  • Distributed System
    • 分布式事务
    • 服务网格
    • BASE理论
    • CAP
    • Etcd
    • Raft协议
    • ZAB协议
  • Go
    • 1.语言基础
      • 1.CPU寄存器
      • 2-1.函数调用
      • 2-2.函数调用栈
      • 2.接口
      • 3.汇编
      • 4.调试
    • 2.编译
      • 1.编译
      • 2.词法与语法分析
      • 3.类型检查
      • 4.中间代码生成
      • 5.机器码生成
    • 3.数据结构
      • 1.数组array
      • 2.切片slice
      • 3.哈希表map
      • 4.字符串
    • 4.常用关键字
      • 1.循环
      • 2.defer
      • 3.panic和recover
      • 4.make和new
    • 5.并发编程
      • 1.上下文Context的实现
      • 2-1.runtime.sema信号量
      • 2-2.sync.Mutex的实现
      • 2-3.sync.WaitGroup
      • 2-4.sync.Once的实现
      • 2-5.sync.Map的实现
      • 2-6.sync.Cond
      • 2-7.sync.Pool的实现
      • 2-8.sync.Semaphore的实现
      • 2-9.sync.ErrGroup
      • 3.定时器Timer的实现
      • 4.Channel的实现
      • 5-1.调度-线程
      • 5-2.调度-MPG
      • 5-3.调度-程序及调度启动
      • 5-4.调度-调度策略
      • 5-5.调度-抢占
      • 6.netpoll实现
      • 7.atomic
    • 6.内存管理
      • 1-1.内存分配基础-TCmalloc
      • 1-2.内存分配
      • 2.垃圾回收
      • 3.栈内存管理
    • 参考
    • 各版本特性
    • 坑
    • Go程序性能优化
    • http.Client
    • net.http路由
    • profile采样的实现
    • Questions
    • time的设计
  • Kafka
    • 高可用
    • 架构
    • 消息队列选型
    • ISR
    • Questions
  • Network
    • ARP
    • DNS
    • DPVS
    • GET和POST
    • HTTP 2
    • HTTP 3
    • HTTPS
    • LVS的转发模式
    • NAT
    • Nginx
    • OSI七层模型
    • Protobuf
    • Questions
    • REST Ful
    • RPC
    • socket缓冲区
    • socket详解
    • TCP滑动窗口
    • TCP连接建立源码
    • TCP连接四元组
    • TCP三次握手
    • TCP数据结构
    • TCP四次挥手
    • TCP拥塞控制
    • TCP重传机制
    • UDP
  • OS
    • 磁盘IO
    • 调度
    • 进程VS线程
    • 零拷贝
    • 内存-虚拟内存
    • 内存分配
    • 用户态VS内核态
    • 中断
    • COW写时复制
    • IO多路复用
    • Questions
  • Redis
    • 安装
    • 参考
    • 高可用-持久化
    • 高可用-主从同步
    • 高可用-Cluster
    • 高可用-Sentinel
    • 缓存一致性
    • 事务
    • 数据结构-SDS
    • 数据结构-Skiplist
    • 数据结构-Ziplist
    • 数据结构
    • 数据类型-Hashtable
    • 数据类型-List
    • 数据类型-Set
    • 数据类型-Zset
    • 数据淘汰机制
    • 通信协议-RESP
    • Questions
    • Redis6.0多线程
    • Redis分布式锁
    • Redis分片
  • System Design
    • 本地缓存
    • 错误处理
    • 大文件处理
    • 点赞收藏关注
    • 短链接生成系统
    • 负载均衡
    • 高并发高可用
    • 规则引擎
    • 集卡活动
    • 秒杀系统
    • 评论系统
    • 熔断
    • 限流
    • 延迟队列
    • Docker
    • ES
    • K 8 S
    • Node.js
    • Questions
  • Work
    • Bash
    • Charles
    • Code Review
    • Ffmpeg
    • Git
    • intellij插件
    • I Term 2
    • Mac
    • mysql命令
    • Nginx
    • postgresql命令
    • Protoc
    • Ssh
    • Systemd
    • Tcp相关命令
    • Vim
Powered by GitBook
On this page
  1. Network

TCP三次握手

一句话:在TCP连接建立过程中,客户端首先向服务端发送SYN报文,服务端接收到SYN报文后会回复一个SYN+ACK报文,客户端再回复一个ACK报文,这样就完成了TCP连接的建立。

详细过程

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

缩写

SYN: Synchronize Sequence Numbers,同步序列号

ISN: Initial Sequence Number,初始同步序列号

第一次握手

客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN。

此时客户端处于 SYN_SENT 状态。

第二次握手

服务器收到客户端的 SYN 报文之后,回复一个 ACK 报文作为应答,把客户端的 ISN + 1 作为 ACK 的值,表示已经收到了客户端的 SYN,约定从数据流从这个数开始。并且指定自己的初始化序列号 ISN。

此时服务器处于 SYN_REVD 的状态。

第三次握手

客户端收到 SYN 报文之后,回复一个 ACK 报文,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 ACK ,并约定数据流从这个数开始。

此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,变为 ESTABLISHED 状态,双方就建立起了连接。

发送第一个 SYN 的一端将执行主动打开(active open),接收这个 SYN 并发回下一个 ACK 的另一端执行被动打开(passive open)

为什么需要三次握手

弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

三次握手的目的是建立可靠的连接,两次握手无法达到这个目的。

第一次握手:客户端发送网络包,服务端收到了。

这样服务端就能得出结论:客户端的发送能力是正常的。

第二次握手:服务端发包,客户端收到了。

这样客户端就能得出结论:服务端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

如果这个阶段就视为连接建立成功,那么:

  1. 后续服务端发送的数据,客户端可能收不到,这是不可靠连接。

  2. 服务端成功建立连接是会为连接创建 fd 并放入全连接队列,但如果客户端的接收能力有问题,那么服务端的资源是浪费的。

第三次握手:客户端发包,服务端收到了。

这样服务端就能得出结论:客户端的接收能力正常。

因此,需要三次握手才能确认双方的接收与发送能力是否正常。

半连接队列

服务器第一次收到客户端的 SYN 之后,会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,称为半连接队列。

虽然叫队列,但半连接队列其实是个 hash表

当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

服务器发送完 ACK 包,如果未收到客户确认包,则会进行退避式重传(间隔变大)。如果重传次数超过配置的最大重传次数,则会将该连接信息从半连接队列中删除。

SYN攻击

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到 SYN 洪泛攻击。SYN 攻击就是 Client 在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用半连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击非常容易,当你在服务器上看到大量的半连接状态的连接时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN 攻击。在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

线上这种情况有四个 TCP 参数可以调,第一个是:tcp_synack_retries 可以用来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大 SYN 连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。第四个方法是开启 cookies 功能,使用 cookie,不使用半连接队列

ISN 是固定的吗?

是动态随机生成的。

当一端为建立连接而发送它的 SYN 时,它会为连接设一个初始序号。ISN 随时间而变化,因此每个连接都将具有不同的 ISN。ISN 可以看作是一个 32 bit 的计数器,每 4ms 加 1 。

这样选择序号的目的在于:

  1. 这个号要作为以后的数据通信的序号,以保证收到的数据不会因为网络上的传输的问题而乱序。如果序号固定,就无法分辨顺序了

  2. 增加安全性。如果序号固定,攻击者很容易猜出后续的确认号

三次握手过程中可以携带数据吗?

其实第三次握手的时候,是可以携带数据的(HTTP3 就是这么做的)。但是,第一次、第二次握手不可以携带数据

假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据,重复发 SYN 报文的话,会大量浪费服务器的资源。

而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

TCP状态机

最后附上一张 TCP 状态机的图。

参考

PreviousTCP连接四元组NextTCP数据结构

Last updated 2 years ago

猿人谷 - 关于三次握手和四次挥手
img
img