继续画图带你学习TCP 其他 7 大特性(四)

pivoteic
发布于 2022-6-14 16:37
浏览
0收藏

 

八、捎带应答 (效率机制)

 

在延迟应答的基础上,为了进一步提高程序运行效率而引入的机制

 

在很多情况下,客户端和服务器的通信模式一般都是 Request - Response 模式,即 “一问一答”

如图:

继续画图带你学习TCP 其他 7 大特性(四)-鸿蒙开发者社区

注意:

三次握手中间的 SYN 和 ACK 都是由内核决定的,不涉及不同的时机

上述提到的四次挥手的过程,ACK 是内核决定的,发的 FIN (close方法) 是应用程序代码决定的

 

九、粘包问题

 

严格说,粘包问题不是 TCP 自身的机制,而是面向字节流传输所具备的共性问题

粘包,指粘的是应用层数据包,导致数据在处理的时候,容易读取半个应用层数据包

 

面向字节流: 指的是一次读一个字节,或者一次读两个字节,或者一次读 N 个字节都行

 

举例:双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构

 

  • “你好不好”
  • “好个P”

 
读多少个字节才是一个完整的应用层数据包,这个是不清楚的

若一次读一个汉字,读出来就是 “好”;若一次读三个汉字,读出来就是 “好个P”

读法不一样,最终的含义差异也很大;读取应用层数据,就不应该只读半个包

 

如何避免粘包问题?

归根结底就是一句话,明确两个包之间的边界

TCP 协议本身不帮你区分应用层数据包,相对而言,UDP 协议没这个问题 (UDP 协议就是按照数据包为单位进行收发的)

 

  • 方式1 - 使用分隔符

 
比如,上述回答改为 “好个P;”

 

用分号 ;当做两个包的分隔符,读数据,一直读到分号;才认为是一个完整的应用层数据包

应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可**

 

  • 方式2 - 明确包的长度

 
比如,上述用例改为 “4你好不好3好个P”

 

先读取最开始的四个字节,得到包的长度3;继续读取3个汉字,于是就读取一个完整的包

 

HTTP 协议基于 TCP 的应用层协议,自己就会处理好粘包问题,上述两种方式都使用到了:

 

对于 GET 请求,分隔符就是空行

对于 POST 请求,Content-length 来指定包的长度

 

思考:对于UDP协议来说,是否也存在 “粘包问题” 呢?

  • 对于UDP,如果还没有上层交付数据,UDP的报文长度仍然在。同时,UDP是一个一个把数据交付给应用层;就有很明确的数据边界
  • 站在应用层的站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收;不会出现"半个"的情况

 
十、保活机制

 

双方建立交互的连接,并不是一直存在数据交互,有些连接会在数据交互完毕后,主动释放连接,而有些不会,那么在长时间无数据交互的时间段内,交互双方都有可能出现掉电、死机、异常重启,还是中间路由网络无故断开、NAT超时等各种意外

 

在这些 “异常情况” 下,TCP 对于连接会有一些特殊的处理

 

举例:

1.进程崩溃: 这种情况,TCP 连接会正常四次挥手 (只要是进程退出,都会自动关闭相关的文件)

2.主机关机(按照流程关机):关机的时候会强制先杀进程,杀进程过程之中就要进行四次挥手了

3.主机断电 / 网线断开:

  • a) 接收方断电。 对端尝试发送消息的时候,就会出现没有 ACK 的情况 — 于是就会触发超时重传 — 重传一定次数,就会重置连接 — 放弃连接
  • b) 发送方断电。对端尝试接收消息,对于接收端来说,本来也不知道发送方什么时候发送,难道就一直等吗?

解决方案 — 心跳包 TCP 的通信双方,即使在没有数据交互的过程中,也会定时相互传输一个没有数据业务意义的 “心跳包”,只是为了证明 “我活着”,一旦隔了一段时间都没有收到对方的心跳包,就可以认为对端"挂"了

 

TCP总结

 

TCP 之所以复杂,是因为它既要保证可靠性,同时又尽可能地提高性能

继续画图带你学习TCP 其他 7 大特性(四)-鸿蒙开发者社区

可靠性:

校验和,序列号

确认应答,超时重传

连接管理,流量控制,拥塞控制

 

提高性能:

滑动窗口,快速重传

延迟应答,捎带应答

 

其他:

定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)

 

文章转自公众号:三友的java日记

标签
已于2022-6-14 16:37:52修改
收藏
回复
举报
回复
    相关推荐