面向连接
三次握手双方的数据包名与连接状态
为什么tcp必须要三次握手才能建立连接
1.从双方连接状态的角度
- 两次握手时,服务端认为该连接不是建立的;该连接还在内核的未完成队列中;
- 即使程序员调用accept函数也不会将连接从内核中获取
2.从客户端接收能力角度
- 客户端发送SYN数据包,对于服务端,收到则认为客户端可以发送;
- 服务端发送ACK+SYN数据包,对于客户端,收到则认为服务端可以接收、发送;
- 客户端发送ACK,对于服务端,收到则认为客户端可以发送;
- 对于客户端,状态是 ESTEBALISHED则连接建立;
- 此时服务端状态是 SYN_RCVD,认为连接未建立;
- 当前连接还在内核未完连接成队列中,即使服务端调用accept也不会将连接从内核中拿回;
- 只有客户端回复ACK,状态变成 ESTEBALISHED,连接才建立,
服务端调用accept函数才能从已完成队列中取回连接
四次挥手双方发送包名与连接状态
四次挥手,双方都有可作为主动断开方
- 主动方发送FIN,状态变成FIN_WAIT1;
- 被动方收到FIN,状态变成CLOSEWAIT(等待关闭连接状态)并回复ACK;
- 主动断开方收到ACK,状态变成FIN_WAIT2;
- 被动方发送FIN,状态从CLOSE_WAIT变成LAST_ACK;
- 主动方收到FIN,状态变成TIME_WAIT,并回复ACK;并等待一会(时间为2MSL)从TIME_WAIT变成CLOSED
- 被动方收到ACK报文,其状态变成CLOSED;
包序管理
要了解包序管理,就要先进行网络抓包:
1.win环境,使用wireshark针对网卡进行抓包 2.linux环境,使用tcpdump,可以抓任意协议的数据包
- 命令:tcpdump -i any port [要抓取的数据端口] -s 0 -w 123.dat
any表示所有网卡都需抓取 - 123.dat抓到的数据所写入的文件
- 将这些文件在wireshark分析
三次握手阶段:三次握手建立连接后,双方并不能正常发送数据
序号:tcp将发送的每个字节数据进行了编号,即序列号
- 双方都有各自的序号,谁发送数据的时候,就使谁的序号标识数据;
- 接收方收到数据后,会进行确认,确认方式是 “期望收到对方发送的下一个序号数据包” ;
- ack是确认收到上一序号消息 + 期望下次收到的信息序号
- seq标识本端是从哪个序号发送信息的
- tcp双方在发送数据时,数据的每个字节都有序号进行标识
纯ACK数据包(上图中第二次连接是ACK+SYN包,第三次连接是纯ACK包)不消耗序号,也就不需要对方确认, 例如:
结论:
- 在三次握手中,双方协商各自维护序号的起始位置
- 双方在发送数据时是消耗自己的序号,确认对方序号
- 纯ACK数据包不消耗序号
- tcp针对发送的数据,每一个字节都会消耗一个序号
四次挥手阶段 MSL:报文最大生存时间(自发送方发送数据后,发送方认为该报文最大生存时间是MSL);
确认应答机制:发送方发送一个数据,需要接收方进行确认;
- 例:主动断开方发送FIN到达对端,对端不回复ACK,则主动断开方没有收到确认就不会认为对端收到FIN报文;
超时重传机制:被动断开方未收到ACK时,重新发送FIN报文的行为
- 主动方发送ACK丢失,被动断开方等待MSL时长后,不清楚自己发送FIN是否到达对端,被动方就会再次发送FIN;
- 主动方收到后重置MSL,产生新的TIME_WAIT状态继续等待2MSL;
正常到达状态:
- 主动断开方等待了2MSL的时间,还未等到重传的FIN报文,说明ACK数据一定到达了被动方;多等一个MSL是保证被动断开方重传FIN能到达主动方
只有主动断开连接方才存在TIME_WAIT状态;
处于TIME_WAIT状态的程序,还需要占用端口,等待2MSL后释放端口,这是要防止被动断开方重传FIN报文
端口重用:
- 主动断开方处于TIME_WAIT状态会等待2MSL时间变成CLOSED,但是这个时间内,这个应用程序虽然退出,但是操作系统还依旧使用着端口(程序中的端口是写死的);
- 在这个时间内再次快启动该程序就会出现问题,报错:bind:Address already in ues
需要在该代码中使用setsockopt函数提前将端口设置为重用状态
CLOSED_WAIT状态
- 被动断开方才有的状态;
- 处于CLOSED_WAIT状态的被动断开方,需要调用close函数关闭新连接套接字,只有调用后才能变成LAST_ACK状态;
- 在这个状态变化过程中,被动断开方还可以给对端发送一些数据
|