TCP的三次握手与四次挥手

TCP的三次握手与四次挥手

关于题目的这个问题,在网络中有着非常重要的地位。因为客户端与服务端之间通过TCP协议,为了进行数据的传输会进行一系列的操作,这中间的操作就是TCP的三次握手与四次挥手。

重新回顾TCP协议

TCP协议称为数据传输协议,是可靠传输,面向连接的,并且面向字节流的。

面向连接:通信之前先建立连接,确保双方在线。

可靠传输:在网络正常的情况下,数据不会丢失。

面向字节流:传输灵活,但是TCP的传输存在粘包问题,没有明显的数据约定。

TCP协议的传输过程

先通过总体图来总的给一个传输过程印象。

网络-三次握手-四次挥手

三次握手

我们已客户端先发送连接请求为前提进行连接。

SYN:同步序列编号(Synchronize Sequence Numbers),是TCP/IP建立连接时使用的握手信号

ACK:确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。

首先客户端进行套接字编程操作创建套接字,再进行绑定地址操作,然后开始向服务端进行连接请求。

与此同时,服务端也创建套接字,也进行绑定地址,然后开始进行监听套接字操作。

握手过程:

客户端先向服务端发送一个SYN消息,此时客户端处于SYN_SEND状态。服务端此时接收到了该消息,这时服务端会发送SYN+ACK,进行消息回复确认。回复完之后,此时服务端处于SYN_RCVD状态,等待客户端进行最后一次确认。这个时候客户端接收到了SYN+ACK包之后,再给服务端发送一个ACK包,此时客户端将处于ESTABLISH状态(就绪状态),代表了客户端此时可以进行收发数据。服务端接受了ACK包之后,此时连接建立成功,服务端也处于ESTABLISH状态,也可以进行收发消息了。

此时客户端与服务端的连接建立完成。它们之间可以进行数据的发送与接收….

但是人有悲欢离合,月有阴晴圆缺,网络迟早断开连接!!!

发送了一段时间后,它们之间要断开连接…

四次挥手

我们再次已客户端先发送断开请求为前提。

FIN:表示断开连接

RST:表示复位,用来异常的关闭连接

挥手过程:

客户端发送完数据之后,关闭了网络套接字,此时客户端向服务端发送了一个FIN包,发送完毕之后,客户端处于FIN_WAIT_1状态。服务端接收到了之后,处于CLOSE_WAIT状态。服务端这时会向客户端发送一个ACK包,发送完了进入LAST_ACK状态进行最后一次等待确认状态,此时伴随着close()关闭套接字。因为服务端还要再确认一次是否断开连接,此时服务端向客户端再发送一个FIN包。客户端接收到了该FIN包之后,再给服务端发送ACK包,代表确认关闭,执行完该操作之后,客户端并没有直接CLOSED并且回收资源,此时客户端处于TIME_WAIT状态,等待时间为两个MSL(最大报文段生存周期,一个大约30s)。服务端此时接收到了该ACK包,进入了CLOSED状态并且回收资源。客户端等待了两个MSL时间之后,也进入了CLOSED状态并且回收资源。

这时客户端与服务端之间断开连接。

TIME_WAIT状态:

在断开的过程中有一个TIME_WAIT状态,为什么会有该状态?

假设没有TIME_WAIT会有什么情况:最后一个ACK丢失,被动关闭方重发FIN包,没有等待直接关闭。若直接启动的客户端此时使用了相同的端口信息,有可能收到FIN包,此时就对新连接造成影响。

若新启动客户端使用了相同的端口信息,向服务器发送了SYN请求,但是服务端因为没有受到最后一个ACK包而一直处于LAST_ACK状态,受到SYN后判定状态错误,回复RST报文重置连接,也对新连接造成了影响。

所以主动关闭方发送最后一个ACK之后需要等待一段时间:两个MSL时间(最大报文等待时间)

1、处理ACK丢失,导致对端重发FIN包

2、等待双方延迟的报文全部消失在网络中,不会对后续连接造成影响。

常见问题

为什么握手是三次,挥手是四次?

在握手过程中,为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

当客户端发出的第一个连接请求报文段并没有丢失,而是在网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达服务端。本来这是一个早已失效的报文段。但是服务端收到此失效的连接请求报文段之后,就误认为是客户端再次发出的一个新的连接请求。于是就向客户端发出确认报文段,同意建立连接。假设我们不采用“三次握手”,那么服务端发出确认,新的连接就建立成功了。由于现在client并没有发出建立连接的请求,因此不会理睬服务端的确认,也不会像服务端发送数据。但是服务端却以为新的建立已经完成,一直等待消息发送到来。这样,服务端的数据很多数据被白白浪费了。采用“三次握手”可以有效的避免这种情况。客户端不会向服务端的确认发出确认,服务端由于收不到确认,就认为客户端没有建立连接。

所以,三次握手也可以看作为了防止服务端的一直等待而浪费资源。

挥手过程是一个相互的确认过程,首先我们都已知TCP的连接是全双工的,既可以发送也可以接收。那么当双方都确认自己没有数据可以发送,并且都互相表示双方知道对方没有数据之后,那么连接也就断开了。

假设挥手在第三次结束。客户端发送了一个FIN就打算断开连接。那么客户端又如何知道服务端想要断开连接?有可能客户端刚发送完了数据,但是服务端并没有。服务端打算发完了剩下的数据在跟客户端”拜拜“。

此时就出现了矛盾。所以挥手有第四次来进行双发的确认。确认双方的工作都做完了。

SYN泛洪攻击

SYN泛洪攻击利用TCP三次握手协议的缺陷,向目标主机发送大量的伪造源地址的SYN连接请求,使得被攻击方资源耗尽,从而不能够为正常用户提供服务。

在TCP协议中被称为三次握手(Three-way Handshake)的连接过程中,如果一个用户向服务器发送了SYN报文,服务器又发出 SYN+ACK 应答报文后未收到客户端的 ACK 报文,这种情况下服务器端会再次发送SYN+ACK给客户端,并等待一段时间后丢弃这个未完成的连接,这段时间的长度称为SYN Timeout,一般来说这个时间是分钟的数量级。

SYN 泛洪攻击所做的就是利用这个SYN Timeout和TCP/IP协议族中的另一个漏洞: 报文传输过程中对报文的源 IP 地址完全信任进行攻击。SYN 泛洪攻击通过发送大量的伪造 TCP 链接报文而造成大量的 TCP 半连接,服务器端将为了维护这样一个庞大的半连接列表而消耗非常多的资源。这样服务器端将忙于处理攻击者伪造的TCP连接请求而无法处理正常连接请求,甚至会导致堆栈的溢出崩溃

造成SYN洪泛攻击最主要的原因是TCP/IP协议的脆弱性。TCP/IP是一个开放的协议平台,它将越来越多的网络连接在一起,它基于的对象是可信用户群,所以缺少一些必要的安全机制,带来很大的安全威胁。例如常见的IP欺骗、TCP连接的建立、ICMP数据包的发送都存在巨大的安全隐患,给SYN洪泛攻击带来可乘之机。

防范方法

1、缩短SYN Timeout时间,短时间内收不到客户端的ACK回复报文直接丢弃,防止被攻击

2、设置SYN cookie,给每一个请求连接的IP地址分配一个cookie,如果短时间内连续受到某个IP的重复SYN文,认定是受到了攻击,以后从这个IP地址来的包会被丢弃。

3、设置SYN可疑队列

4、使用防火墙

推荐博客链接:https://blog.csdn.net/justenjoyitpy/article/details/78151239

若服务端出现了大量的TIME_WAIT连接,为什么?如何解决?

这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,在四次的挥手过程中我们已经可以了解TIME_WAIT状态的出现情况。

1、防止上一次连接的包迷路后重新出现,影响新连接,所以等待被动方发来的FIN包

2、可靠的关闭TCP连接。在主动方发送的最后一个ACK,有可能丢失,这时被动方会重新发FIN包,如果这时主动方处于CLOSED状态,就会响应RST而不是ACK。所以主动方要处于TIME_WAIT,而不能是CLOSED。

解决的方案就是让服务器能够快速回收和重用那些TIME_WAIT资源

这里推荐一篇博客链接:https://www.cnblogs.com/whx7762/p/9413787.html

下面是一篇文章中提到的/etc/sysctl.conf文件的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
net.ipv4.tcp_synack_retries=2
//表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
//表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
//表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数
net.ipv4.tcp_max_syn_backlog = 4096
//表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
//表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
//表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关
net.ipv4.tcp_tw_recycle = 1
//减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
//优化网络设备接收队列
net.core.netdev_max_backlog=3000

修改完之后执行/sbin/sysctl -p让参数生效。

net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。

net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。

net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

在TCP的传输过程中最重要的就是三次握手与四次挥手,但是TCP的传输过程是面向可信任群体的,安全性相对较弱,会受到一些攻击,不过有方法可以解决。都是根据传输过程进行范围内的防御措施。

-------------The End-------------