【计网】TCP的三次握手及四次挥手详解
引入
运输层连接有三个阶段
- 连接建立(三次握手)
- 数据传送
- 连接释放(四次挥手)
三次握手原理
1.图解三次握手建立 TCP 连接的各状态
2.建立连接过程分析
(1)第一次握手
A 向 B 发出连接请求报文段,其首部中的同步位 SYN = 1,并选择序号 seq = x,表明传送数据时的第一个数据字节的序号是 x。
(2)第二次握手
B 收到连接请求报文段后,同意A的请求,发回确认,B 在确认报文段中设置SYN = 1,ACK = 1,确认号ack = x+1,自己选择的序号 seq = y
(3)第三次握手
A收到此报文段后向 B 给出确认,设置 ACK = 1,确认号 ack = y +1,然后 A 通知上层应用进程,连接已经建立。
3.建立连接的状态转换分析
(1)第一次握手
客户端:CLOSED–>SYN-SENT
客户端之前处于关闭状态,需要传送数据时,向服务端发送建立连接请求,转换为SYN-SENT,表示建立连接请求报文段已发送。服务端:CLOSED–>LISTEN
服务端之前处于关闭状态,由于服务端需要接收客户端发来的数据,所以转换为LISTEN状态,表示处于监听状态
(2)第二次握手
客户端:SYN-SENT–>ESTABLISHED
客户端接收到了服务端的请求报文,然后转换为ESTABLISHED状态,表示连接已建立。服务端:LISTEN–>SYN-RCVD
服务端接收到了客户端的请求报文,也给客户端发送请求连接报文,之后等待客户端确认,转换为SYN-RCVD,表示已接收到客户端的请求。
(3)第三次握手
客户端:SYN-SENT–>ESTABLISHED
给服务端发送确认信息,然后转换为ESTABLISHED状态,表示连接已建立。服务端:SYN-RCVD–>ESTABLISHED
接收到了客户端发来的确认之后,就转换状态为ESTABLISHED,表示已经建立连接。
4.建立连接中的几个问题
(1)第一次握手中的seq有什么作用,是不是固定的值?
答:序号seq可以保证有序性,当序号达到最大值时(2的32次方:大约4G),可以重用,这个初始序号会随时间而改变,因此每一个连接都拥有不同的初始***(防止重叠),这样也保证了安全性,若序号是固定值,则很容易被伪造,从而打断TCP的正常连接。
(2)为什么第二次握手中的ack=x+1?
答:因为第一次握手中的SYN请求报文段不携带数据,但要消耗一个序号,所以ack=x+1。
(3)为什么不是两次握手?
答:防止已失效的连接请求报文段重连,还会浪费服务器资源,因为失效的报文段会创建文件描述符从而消耗内核结构体。
四次挥手原理
1.图解四次挥手释放 TCP 连接的各状态
2.释放连接过程分析
(1)第一次挥手
A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的 FIN = 1,其序号seq = u,等待 B 的确认
(2)第二次挥手
B 发出确认,确认号 ack = u+1,而这个报文段自己的序号 seq = v。TCP 服务器进程通知高层应用进程。从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状态,B 若发送数据,A 仍要接收。
(3)第三次挥手
若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接,请求关闭连接标志位FIN=1,ACK=1,确认上一个报文已收到,可能还有数据发送,所以seq=w,确认号ack=u+1不变。
(4)第四次挥手
A 收到连接释放报文段后,必须发出确认,在确认报文段中 ACK = 1,确认号 ack=w+1,自己的序号 seq = u + 1。
3.释放连接的状态转换分析
(1)第一次挥手
客户端:ESTABLISHED–>FIN-WAIT-1
客户端之前处于建立连接状态,当不需要传送数据时,向服务端发送关闭连接请求,转换为FIN-WAIT-1,表示请求关闭连接报文段已发送。处于等待关闭状态。服务端:ESTABLISHED–>CLOSE-WAIT
服务端之前处于建立连接状态,由于客户端不需要给服务端发送数据了,并且向服务端发送了关闭连接请求,但这时候服务端可能还要向客户端发送数据,所以是半关闭状态,转换为CLOSE-WAIT状态,表示处于等待关闭状态。
(2)第二次挥手
客户端:FIN-WAIT-1–>FIN-WAIT-2
客户端接收到了服务端的确认报文,然后转换为FIN-WAIT-2状态,表示处于等待服务端关闭连接状态。服务端:ESTABLISHED–>CLOSE-WAIT
服务端给客户端发送确认报文,转换为CLOSE-WAIT,表示处于等待关闭状态。
(3)第三次挥手
客户端:FIN-WAIT-2–>TIME-WAIT
客户端收到了服务端的请求关闭连接报文,转换为TIME-WAIT,等待2MSL就关闭连接,表示处于TIME-WAIT状态。服务端:CLOSE-WAIT–>LAST-ACK
这时候服务端没有数据要给客户端发送了,就请求关闭与客户端的连接,但还要等待客户端的确认,所以就转换为LAST-ACK状态,表示处于等待客户端的确认状态。
(4)第四次挥手
客户端:FIN-WAIT-2–>TIME-WAIT
客户端给服务端发送确认报文,之后转换为TIME-WAIT,等待2MSL就关闭连接,表示在TIME-WAIT状态之后就处于CLOSED状态。服务端:LAST-ACK–>CLOSED
接收到了客户端发来的确认报文之后,就转换为CLOSED状态,表示处于关闭连接状态。
4.释放连接中的几个问题
(1)为什么第二次挥手没有FIN?
答:因为TCP是全双工的,服务端可能还要给客户端发送数据。
(2)为什么第三次挥手中还需要ACK=1
答:因为要给客户端通知已经收到了上一次的数据,虽然数据传送客户端的确认由内核(OS)完成。
(3)第四次挥手之后,为什么需要等待2MSL,等待时间是否可以调整(调大或调小有什么影响)?
答:
- 因为要可靠地实现TCP全双工连接的终止(若最后一次ACK丢失,重发可能丢失的ACK报文)
- 允许老的重复分组在网络中消逝(TCP防止某个连接的老的重复分组在该连接终止后再现),即保证被重新分配的socket不会受到之前残留的延迟重发报文影响,防止某个连接的重复报文在连接终止后出现。
- Windows下默认为4分钟,即240秒
(4)若出现大量TIME-WAIT状态的原因及解决方法?
答:
原因:TIME_WAIT状态下的socket不能被回收使用.
- 对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 这将会占用内存,消耗CPU。
解决方法: 查看当前time_wait的数量 netstat -an | grep TIME_WAIT | wc -l
-
Windows:
在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters,添加名为TcpTimedWaitDelay的DWORD键,设置为60,以缩短TIME_WAIT的等待时间
-
Linux:
- 1)通过调整内核参数解决,vi /etc/sysctl.conf,增加或修改如下参数
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies,可防范少量SYN攻击
net.ipv4.tcp_tw_reuse = 1 表示开启TIME-WAIT sockets重用
net.ipv4.tcp_tw_recycle = 1 开启TCP连接中TIME-WAIT sockets的快速回收
net.ipv4.tcp_fin_timeout = 30 修改系統默认的 TIMEOUT 时间
执行 /sbin/sysctl -p 让参数生效,查看系统的tcp参数情况:sysctl -a|grep tcp - 2)还可以设置/etc/sysctl.conf下的net.ipv4.ip_local_port_range的参数至少为1024 65535
- 1)通过调整内核参数解决,vi /etc/sysctl.conf,增加或修改如下参数
TCP 的有限状态机
注:红色箭头表示对客户进程的正常变迁,蓝色箭头表示对服务器进程的正常变迁,黑色箭头表示异常变迁。
小疑问
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
答:因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2.服务端主动断开连接之后,可以立即使用原来的端口重新连接吗?
答:不能,立即重连产生异常,因为该端口已被占用,TIME-WAIT时间还没到,产生的“分身”会在2MSL之后消亡。
3.当输入端口号为0或-1(负数)时会出现异常吗?
答:当端口号为负数会出现异常,负数会报IllegalArgumentException:port out of range异常,0-1023是熟知端口,可能会出现BindException: Address already in use: bind,所以我们一般使用1024-65535之间的端口号。
4.半连接队列与全连接队列是什么?
在linux系统内核中维护了两个队列:syns queue和accept queue
syns queue
用于保存半连接状态的请求,其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,一般默认值是512,不过这个设置有效的前提是系统的syncookies功能被禁用。互联网常见的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求。accept queue
用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。
如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。
5.SYN攻击是什么及解决办法?
答:
(1)概念
SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。
(2)检测
使用Linux命令 netstat -n -t | grep SYN_RECV | wc -l 查看处于SYN_RECV状态的连接数
(3)解决方法
- 过滤网关防护
- 网关超时设置:防火墙设置SYN转发超时参数,该参数远小于服务器的timeout时间
- SYN网关:CS通过SYN网关发SYN包,当客户端确认包到达时,如果有数据则转发,否则丢弃。
- SYN代理:不转发SYN包,而以服务器的名义主动回复SYN/ACK给客户,若收到ACK包,正常访问。
- 加固tcp/ip协议栈
- SynAttackProtect机制:关闭某些socket选项,使系统能处理更多的SYN连接
- 增加最大半连接数:Linux下用变量tcp_max_syn_backlog定义backlog队列容纳的最大半连接数
- SYN cookies技术:当半连接队列满时,SYNcookies不丢弃SYN请求,而通过加密技术来标识半连接状态
本人才疏学浅,若有错,请指出,谢谢!
如果你有更好的建议,可以留言我们一起讨论,共同进步!
衷心的感谢您能耐心的读完本篇博文
参考资料:《计算机网络 第六版》-谢希仁