文章目录
三次握手
TCP三次握手是浏览器和服务器建立连接的方式,目的是为了使二者能够建立连接,便于后续的数据交互传输。
第一次握手:浏览器向服务器发起建立连接的请求
第二次握手:服务器告诉浏览器,我同意你的连接请求,同时我也向你发起建立连接的请求
第三次握手:浏览器也告诉服务器,我同意建立连接。
至此,双方都知道对方同意建立连接,并准备好了进行数据传输,也知道对方知道自己的情况。接下来就可以传输数据了
简单示意图
一次握手:客户端发送带有SYN 标志的连接请求数据包给服务端
二次握手:服务端发送带有SYN+ACK 标志的连接请求和应答数据包给客户端
三次握手:客户端发送带有ACK 标志的应答数据包给服务端(可以携带数据了)
详细分析

0、初始状态:
服务端监听某个端口,处于LISTEN 状态。
1、客户端发送TCP连接请求
客户端会随机一个初始序列号seq=x(client_isn),
设置SYN=1 ,表示这是SYN握手报文。然后就可以把这个 SYN 报文发送给服务端了,表示向服务端发起连接,之后客户端处于同步已发送 状态。
2、服务端发送针对TCP连接请求的确认
服务端收到客户端的 SYN 报文后,也随机一个初始序列号(server_isn)(seq=y)
设置ack=x+1, 表示收到了客户端的x之前的数据,希望客户端下次发送的数据从x+1开始。
设置 SYN=1 和 ACK=1。表示这是一个SYN握手和ACK确认应答报文。
最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于同步已接收 状态。
3、客户端发送确认的确认
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,
将ACK置为 1 ,表示这是一个应答报文
ack=y+1 ,表示收到了服务器的y之前的数据,希望服务器下次发送的数据从y+1开始。
最后把报文发送给服务端,这次报文可以携带数据,之后客户端处于连接已建立 状态。
服务器收到客户端的应答报文后,也进入连接已建立 状态
一些思考
为什么要三次握手,而不是两次?
原因主要有两个:
1、主要原因是为了防止历史连接
三次握手时,在网络拥堵等情况下,第一次握手的SYN包迟迟没能发送到服务端,那么客户端会连续发送多次 SYN 建立连接的报文,那么就可能出现一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
那么此时服务端就会回一个 SYN + ACK 报文给客户端;
如果是两次握手连接,就不能判断当前连接是否是历史连接,导致错误。
三次握手时,客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,中止这一次连接。
2、三次握手可以避免资源浪费
如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,
- 如果是三次握手,第三次握手时服务器可以得到客户端的ack,知道连接已成功建立。
- 如果没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,如果客户端的 SYN 阻塞了,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
SYN 攻击
什么是SYN 攻击?
SYN攻击属于DoS攻击(Denial of Service 拒绝服务)的一种。
SYN攻击大量发送伪造源IP的第一次握手SYN包,服务器每接收到一个SYN包就会为这个连接信息分配核心内存并放入半连接队列,当攻击的SYN包超过半连接队列的最大值时,正常的客户发送SYN数据包请求连接就会被服务器丢弃。导致正常的连接请求无法成功。
严重者引起网络堵塞甚至系统瘫痪。
正常流程:
当服务端接收到客户端的 SYN 报文时,会将其加入到内核的「 SYN 队列」;
接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
服务端接收到 ACK 报文后,从「 SYN 队列」移入到「 Accept 队列」;
应用从「 Accept 队列」取出连接。
受到 SYN 攻击:
如何防止SYN 攻击?
1、限制ip连接次数:比如限制同一IP一分钟内新建立的连接数仅为10
2、增大半连接状态的连接数容量
但是增大内存资源占用,不推荐。
3、延迟分配连接资源
当服务器收到第一次握手请求时,不马上分配TCP连接资源。而是计算一个随机值,在第二次握手时传给客户端,当客户端返回第三次握手时,服务器验证随机值的正确性,确认无误才会进入 TCP 的连接状态,才会分配资源。
如果是恶意攻击者发送的大量SYN报文,只要服务器不为其分配资源,也不至于遭到严重破坏了。
数据包丢失了该怎么办?
1、TCP 第一次握手的 SYN 丢包了,会发生什么?重传 SYN 数据包,重传次数超过阈值后放弃
2、TCP 第二次握手的 SYN、ACK 丢包了,会发生什么?
客户端 SYN 包没有收到ACK,所以会超时重传
服务端 SYN包也没有收到ACK, 也会超时重传。
3、TCP 第三次握手的 ACK 包丢了,会发生什么?
服务端会一直重传 SYN、ACK 包,重传次数超过阈值后放弃
而客户端根据是否开启保活机制分为两种情况:
开启了保活机制的话,会经过
2 小时 11 分 15 秒发现一个「死亡」连接,于是客户端就会断开连接。没有开启的话,则会一直重传该数据包,直到重传次数超过阈值后就会断开 TCP 连接。
初始序列号为什么随机产生?
为了网络安全
如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间通信的初始化序列号,并且伪造序列号进行攻击,这已经成为一种很常见的网络攻击手段。
为什么 SYN 段不携带数据却要消耗一个序列号呢?
因为SYN 段需要对方的确认,所以需要占用一个序列号确保这个确认不会出现歧义。如果不占序列号的话,怎么知道这个确认是对数据包的确认还是对syn报文的确认呢?
每次握手可以确定哪些东西?
第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
第二次握手:Client 确认了自己发送、接收正常,对方发送、接收正常;
第三次握手:Server 确认了自己发送正常,对方接收正常
一个已经建立的 TCP 连接中,客户端中途宕机了,客户端恢复后,向服务端发送SYN包重新建立连接,此时服务端会怎么处理?
我们知道TCP 连接是由「四元组」唯一确认的。
然后这个场景中,客户端的IP、服务端IP、目的端口并没有变化
所以这个问题关键在于:本次连接的源端口是否和上一次连接的源端口相同。
所以分两种情况:
1、不相同
此时服务端会认为是新的连接要建立,于是就会通过三次握手来建立新的连接。
那旧连接里的服务端会怎么样呢?
如果服务端发送了数据包给客户端,由于客户端的连接已经被关闭了,此时客户的内核就会回RST报文,服务端收到后就会释放连接。
如果服务端一直没有发送数据包给客户端,在超过一段时间后, TCP保活机制就会启动,检测到客户端没有存活后,接着服务端就会释放掉该连接。
2、相同
处于 establish 状态的服务端会回复一个携带了对上次报文的确认号和序列号,这个 ACK 被称之为 Challenge ACK。
接着,客户端收到这个 Challenge ACK,发现序列号并不是自己期望收到的,于是就会回RST 报文,服务端收到后,就会释放掉该连接。
如何手动关闭一个TCP连接
结论:伪造一个能关闭 TCP 连接的 RST 报文
这个合法的 RST 报文必须同时满足「四元组相同」和「序列号正好落在对方的滑动窗口内」这两个条件。
怎么伪造?
直接伪造符合预期的序列号是比较困难,因为如果一个正在传输数据的 TCP 连接,滑动窗口时刻都在变化,因此很难伪造一个刚好落在对方滑动窗口内的序列号的 RST 报文。
办法还是有的,我们可以伪造一个四元组相同的SYN 报文,来拿到“合法”的序列号!
怎么拿到?
如果处于 establish 状态的服务端,收到四元组相同的 SYN 报文后,会回复一个 Challenge ACK,这个 ACK 报文里的「确认号」,正好是服务端下一次想要接收的序列号,说白了,就是可以通过这一步拿到服务端下一次预期接收的序列号。
然后用这个确认号作为 RST 报文的序列号,发送给服务端,此时服务端会认为这个 RST 报文里的序列号是合法的,于是就会释放连接!