TCP是面向连接的,那怎样去实现我们的面向连接呢,这时候就是三次握手建立连接以及四次挥手关闭连接。如下图
三次握手过程:首先发起连接请求的是客户端,给服务器端发送一个SYN请求,并且可以携带一定的数据,也可以不用携带,在我们的服务端收到一个SYN请求的时候会给客户端回复一个ACK+SYN,ACK作用就是服务端确定收到了这么一个请求,SYN是和客户端建立一个连接请求,在客户端收到请求之后会发送一个ACK请求,这里我们就三次握手即完成了。
服务端状态改变:服务端在从CLOSED状态到我们的SYN_RCVD就是我们创建套接字,绑定我们的ip信息,设置监听,在设置监听之后服务端才能对SYN请求包进行接收,在接收到一个SYN请求之后服务端状态就变为了SYN_RCVD状态。就表示我们已经接收到了客户端发来的请求。在发送SYN+ACK之后客户端回复之后服务端就变为ESTABLISHED状态。
客户端状态接收
客户端从CLOSED状态变为SYN_SENT状态就是我们建立一个套接字,并且进行connect()之后就变为SYN_SEND状态,,在发送SYN之后收到服务端回复之后就变为ESTABLISHED状态。
只有客户端和服务端都处于ESTABLISHED状态的时候才能进行数据通信
数据通信:就是进行send和recv操作,在上图中是客户端发送数据,服务端需要回复一个ACK,表示这已经成功接收到了数据
四次挥手
这和三次握手不一样,三次握手是客户端发起的SYN请求,在四次挥手中客户端和服务端都能发起断开连接请求。比如上图中。首先客户端给服务端发起一个FIN包,表示我要和你断开连接,在服务端收到FIN包的时候回复一个ACK表示我已经收到了你的请求,并且给客户端也发送一个FIN包,表示我也要和你断开连接,客户端收到FIN后回复给服务端一个ACK,当服务端收到之后连接就此断开。
客户端状态变化
当客户端发起FIN就是我们close()
产生的。当调用close()
之后客户端变为FIN_WAIT1状态在发送FIN包之后服务端收到之后并且发起一个ASK应答,在客户端收到之后变为FIN_WAIT2状态,到服务端也发起一个FIN请求,在客户端收到之后就变为TIME_WAIT状态,当服务端CLOSE之后客户端也进入CLOSED状态。
服务端状态变化
当接收到客户端发出的FIN请求的之后服务端变为CLOSE_WAIT状态,在回复ACK应答之后给客户端发起一个FIN之后变为LAST_ACK,知道最后变为CLOSED状态
特殊状态
在三次回收四次握手中有两个特殊的状态,一个是ESTABLISHED和TIME_WAIT状态,并且是必不可少的。
相关问题
(1)为什么握手是三次,挥手是四次呢?
握手是三次是确保可靠的基本需要,因为2次是不能建立连接的,但是四次又太多余了,ACK和SYN是可以一起发送的。就像我们打电话一样,接通之后需要确定双方都能听到对方的声音。
挥手是四次:当被动方收到主动方报文通知时,他仅仅表示主动方没有数据在发送给被动方了,但是未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET,他可能还需要发送给主动方一些数据再发送FIN报文给主动方,告诉主动方统一关闭连接。所以这里的FIN和ACK是分开发送的。
(2)若三次握手第三次握手失败,服务端如何去处理?
第三次握手失败之后并不会重传ACK报文,而是直接发送RTS报文段,进入CLOSED状态,这样的目的就是为了防止SYN泛洪攻击
(3)SYN泛洪攻击是什么,怎样去预防?
SYN泛洪攻击就是利用TCP三次握手机制,攻击端利用伪造的IP地址向被攻击端发出请求,而被供给端发出的响应报文将永远发送不到目的地,那么背攻击端在等待关闭这个连接的过程中消耗了资源,当连接成千上万的时候主机资源被消耗完,服务端就瘫痪了。
预防
(4)TIME_WAIT的作用(在上面已经讲解过了)
(5)若服务端出现了大量的TIME_WAIT状态,为什么?如何解决?
只有在主动关闭方才会出现TIME_WAIT状态,一般来说都是客户端主动关闭,服务端不会出现TIME_WAIT状态,但是有一些特殊的服务,比如pop/smtp,ftp却是服务端收到客户端的QUIT命令之后主动关闭连接,这就造成服务端出现大量的TIME_WAIT状态,
解决办法:让服务器能够快速回收和重用那些TIME_WAIT的资源,修改参数,设置端口复用。将TIME_WAIT的时间设置小点。就可以减少TIME_WAIT的状态数量
(6)服务端保持大量的CLOSE_WAIT状态,为什么?如何解决? 出现原因:对方在关闭连接之后服务器程序自己没有进一步发出ACK信号,换句话说就是在对方连接关闭之后程序里没有检测到,或者程序压根就忘记这个时候需要关闭连接,所以这个资源一直被程序占用。(对方连接的异常或者自己没有迅速的回收资源) 解决办法:查看代码,这是代码中出现的错误。比如没有关闭套接字等等。