目的
三次握手为了安全的建立连接,四次分手为了安全的断开连接
三次握手
套接字:所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一般用ip:端口号表达一个套接字。
套接字发送码说明
状态 | 说明 |
---|---|
SYN | 同步序列编号(Synchronize Sequence Numbers),一般是主动发起连接请求的一方先发出,为1表明有建立连接的意愿 |
ACK | ACK (Acknowledge character)即是确认字符,一般是接收到SYN的接收方返回给发送方,为1表明确认 |
seq | 占 4 字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。说白了seq就是报文头的第一个数据字节的序号,用来定位数据的 |
ack | 占 4 字节,期望收到对方下个报文段的第一个数据字节的序号。 |
套接字状态说明
状态 | 说明 |
---|---|
LISTENING | FTP服务启动后,两个套接字一开始都是侦听(LISTENING)状态。 |
SYN_SENT | 套接字正在试图主动建立连接[发送SYN后还没有收到ACK] |
SYN_RECEIVED | 正在处于连接的初始同步状态[收到对方的SYN,但还没收到自己发过去的SYN的ACK] |
ESTABLISHED | 连接已经建立 |
握手流程
- 第一次握手:
- A向B发送一个握手请求,表示我想要建立连接
- 此时A的状态变成了SYN_SENT状态
- 发送的SYN=1表明数据包的状态,A想建立连接,seq=x表明第一个数据字段的序号。
- 此时B收到了A的消息,B机器确认了A的发报能力和B的收报能力是正常的。
- 第二次握手:
- B收到A的握手请求,对A的请求做出响应,同意建立连接
- B变为了SYN_RCVD状态
- B向A响应一个标志SYN=1&ACK=1的数据包,表达自己也想建立连接且对A的请求做回复,seq=y表明B响应的包中第一个数据字段的序列号,ack=x+1表明B希望A下次发送的数据字段的序列号。
- 此时A收到了B的消息,A机器确认了A的发报能力和A的收报能力、B的收报能力以及B的发报能力正常
- 第三次握手
- A收到B的回复,对B的回复做出确认
- A变成ESTABLISHED状态
- 发送一个状态为ACK=1的数据包,对B想建立连接的请求做个回复。seq=x+1是对二次握手中B的ack做回复,ack=y+1是表明A希望B下次发送的数据字段的序列号。
以此类推,下一次信息交流,B发给A的seq=y+1,ack=x+2,然后再下一次A发给B的seq=x+2,ack=y+2…seq对上一次的ack做回应,ack根据这次收到的seq做+1
- B收到回复,变为ESTABLISHED状态,连接建立。
三次握手的必要性
- 信息对等
- 防止超时造成的脏连接
如果少了一次握手,发生下图这种情况
如果二次握手后,不需要三次握手,那么这个时候其实A和B已经建立了一条成功的连接。
但之后A超时的请求到达了,B以为这是一条新的第一次握手请求,于是发送一个响应。
这个时候A这面已经是ESTABLISHED状态,当然直接忽略了这个请求。
由于不需要经过第三次握手,B不关心A的回复,所以想当然的认为A收到且同意了,于是一条脏链接建立了起来。
四次分手
套接字发送码说明
状态 | 说明 |
---|---|
FIN | 与SYN相反,表达想要断开连接的请求,二者不会同时为1 |
套接字状态说明
状态 | 说明 |
---|---|
FIN_WAIT_1 | 套接字已关闭,正在关闭连接[发送FIN,没有收到ACK也没有收到FIN] |
FIN_WAIT_2 | 套接字已关闭,正在等待远程套接字关闭[在FIN_WAIT_1状态下收到发过去FIN对应的ACK] |
CLOSE_WAIT | 远程套接字已经关闭:正在等待关闭这个套接字[被动关闭的一方收到FIN] |
CLOSING | 套接字已关闭,远程套接字正在关闭,暂时挂起关闭确认[在FIN_WAIT_1状态下收到被动方的FIN] |
LAST_ACK | 远程套接字已关闭,正在等待本地套接字的关闭确认[被动方在CLOSE_WAIT状态下发送FIN] |
TIME_WAIT | 这个套接字已经关闭,正在等待远程套接字的关闭传送[FIN、ACK、FIN、ACK都完毕,这是主动方的最后一个状态,在过了2MSL时间后变为CLOSED状态] |
CLOSED | 没有使用这个套接字[netstat 无法显示closed状态] |
握手流程
与建立连接不同的时,断开连接时,协议层需要等应用层反馈,毕竟连接已经在使用了,所以有了各种wait状态
- 第一次分手:
- A一开始处于ESTABLISHED状态
- A想要主动断开连接,于是给B发送FIN=1,seq=u
- 由于A是主动的,所以是在应用层断开连接后才发送的请求,A的状态变成了FIN_WAIT_1
- 第二次分手:
- B一开始处于ESTABLISHED状态
- B收到A的请求,发送ACK=1,seq=v,ack=u+1,表达自己同意断开连接
- B的状态变成了CLOSE_WAIT,正在等待应用层关闭连接
- 第三次分手:
- A收到B的ACK请求,明白了B同意分手
- A进入FIN_WAIT_2状态,等待B发来关闭连接的确认,也就是FIN请求
- B的应用层关闭了请求后,发送FIN=1,ACK=1,seq=w,ack=u+1的FIN请求
- B变为了LAST_ACK状态
- A在FIN_WAIT_2状态下等到B的FIN请求,明白B已经同意分手且做好了所有分手准备,于是发送ACK=1,seq=u+1,ack=w+1的ACK请求做回复
还有种情况是由于网络原因,B的FIN请求比ACK请求先到了,所以A是在FIN_WAIT_1状态下接受到的FIN请求,这个时候A直接变为CLOSING状态
- A自己进入一个TIME_WAIT状态,此时A其实已经可以单方面关闭了,只不过留一点时间等待三次分手的请求发送,这个时间如果B没回复没啥变故的话,时间过后变为CLOSED状态
- 第四次分手:
- B收到A的ACK请求后,知道了A已经马上要关闭或者已经关闭了
- B直接关闭,变为CLOSED
四次分手中,两个wait状态需要重点记忆下,CLOSE_WAIT是被动关闭的一方在等待应用层断开连接的一个状态,TIME_WAIT主动方等待一定时间给被动方反应后,直接断开TCP连接的一个状态
四次分手必要性
其实和三次握手是同样的道理,网络传输过程中会有各种超时等等的不确定性,都是为了防止这些不确定性而上的保险
比如没有最后一次分手,B发送完FIN请求后直接断开,反正B知道了A的FIN为1,表明A做好了断开准备,我B自己发送了FIN=1,也做好了准备,讲道理自然可以单方面断开连接了。
但是,如果这个FIN请求由于网络原因,没发到A,这个时候B已经断开连接了,但是A还在苦苦等待B的FIN请求,不能断开。
所以造成了一条废弃的连接。