思考:
长连接为啥需要心跳?
这是由于网络通讯中,存在的中间节点设备,可能具有自动超时关闭作用,为了防止被自动关闭,需要一定时间后刷新链路。
TCP三次握手(SYN)
- 1、A->B:A发送syn=1(请求类型标识)、随机seq number到服务器B。syn码=1标识当前是希望建立连接,seq码可以认为是身份唯一标识字段。–自己进入SYN_SEND状态。
- 2、B->A:B查看syn发现=1则知道是想建立连接,若运行联机,则向A发送:ack=1(响应类型)、ack number=(主机A的seq+1)关联身份唯一标识,syn=1,seq number随机数据包。–自己进入SYN_RECV状态。
- 3、A->B:A客户端检查ack number(是否为seq+1)以及ack是否为1,若都正确,则再次发送ack number=(主机B的seq+1)且ack=1,B接受到后校验通过则完成连接–客户端和服务器端均进入ESTABLISHED状态。
每次都是先检查ack是否被对方正常确认,若确认则取对方seq码+1作为响应序列让对方返回时做身份认证,并自己也随机生成一个seq码让对方使用。当然SYN码是一直携带的,标识这是一个正在建立联机。
TCP四次分手(FIN)
- 1、A->B:A发送一个FIN和seq,告诉B需要关闭连接。
- 2、B->A:B发送ACK和ackNumber=(seq+1),确认收到关闭请求。
- 3、B->A:B发送FIN和ackNumber=(seq+1)和随机seq,表示自己没有数据可发送了,你可以关闭了。
- 4、A->B:A发回ACK报文确认
不管是握手还是分手,应答方都是ACK标识。因为 TCP 连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭。这个单方向的关闭就叫半关闭。当一方完成它的数据发送任务,就发送一个 FIN 来向另一方通告将要终止这个方向的连接
TCP可靠性控制
- 1、应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。(将数据截断为合理的长度)
- 2、当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
- 3、对包做首部和数据和校验,保证数据没有被串改,破损。
- 4、对收到数据包进行重排序,恢复正确顺序后再转交给应用层。
- 5、TCP做流量控制,TCP的两端都使用缓冲区块,这样防止发送端发送消息过快导致接收端缓冲区溢出。
TCP流量控制和拥塞控制
滑动窗口:发送方的发送窗口不能超过接收方给出的接收窗口的数值。
在建立连接阶段,发送方和接受方共同约定一个固定大小的窗口,发送端可以连续发送N个长读的字符
- 若发送的长度超过接收端的允许窗口大小,则将超过长度的那个包直接丢弃。接收端在ACK应答时将允许发送的起始位置和允许发送的长度返回给发送端,后面发送端依据应答参数调整自己的发送起始位置和发送量。
- 若ACK应答的可发送量(空闲窗口)为rwind=0,则发送端将停止发送。只要一方接受到rwind=0,则发送方就启动持续计时器,若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
TCP粘包&拆包
TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据。TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。
粘包问题的解决策略
由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决。业界的主流协议的解决方案,可以归纳如下:
- 消息定长,报文大小固定长度,例如每个报文的长度固定为200字节,如果不够空位补空格;
- 包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符作为报文分隔符,接收方通过特殊分隔符切分报文区分;
- 将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段;
- 更复杂的自定义应用层协议。