websocket功能背景
websocket为web应用的客户端和服务端提供了一种全双工的通信机制,底层依赖TCP连接,为保证高可用和及时性,不得不设计一套保活、验活、重连的方案。 本文从实践过程中对重连存在的疑问进行分析。
测试项目
服务端 springboot+websocket
客户端测试
通过api接口模拟服务端断开连接
http://localhost:8081/close?userId=liuyanqun
异常场景总结
场景1(断网恢复接收消息)
- websocket客户端(web页面)断网
- websocket服务端发送消息给客户端
- 客户端断网等待一段时间(测试等待1个小时)在恢复网络
- 客户端还可以监听到了服务端的消息
分析原因
websocket断开通常依赖于超时机制或者明确的关闭指令
web页面断网后,无法主动通知TCP断开连接,所以需要心跳验活机制保证双端正常通信。
websocket底层基于TCP,TCP协议支持保持连接(Keepalive)所以一定时间内断网恢复网络后还能收到服务推送的消息。TCP本身有超时机制时长影响因素比较多(网络断开的时间长度、TCP 超时设置以及具体的 WebSocket 实现方式)。我测试所有都是默认设置,测试断网1个小时恢复网络,依然可以收到服务端之前推送的消息。网上有说默认TCP超时2个小时,后面找个时间在验证下。
场景2(断网断开socket连接)
- websocket客户端(web页面)断网
- websocket服务端发送消息给客户端,服务端管理socket连接
- 客户端断网等待一段时间(测试等待10分钟)在恢复网络
- 客户端还可以监听到了服务端的消息,和断开socket消息
分析原因
websocket客户端断网期间,服务端通过channel发送消息,以及断开连接发送的关闭帧,实际上都是消息。这些消息存储在操作系统的TCP和IP栈。
对于 WebSocket 来说:
- 当服务端通过 WebSocket 发送消息给客户端时,这个消息实际上是通过底层的 TCP 连接发送的。因此,如果客户端由于断网或其他原因没有发送确认应答,服务端的 TCP 栈会根据超时设置重传消息。
- 客户端的 TCP 栈也会缓存它收到的 TCP 消息,直到应用程序层(在这种情况下是 WebSocket 客户端)读取这些数据。如果客户端应用程序由于某种原因(如崩溃或断网)没有读取这些数据,TCP 栈仍然会保持这些数据直到它们被读取或者连接被关闭。
- 如果客户端断网,它将无法发送确认应答给服务端,服务端的 TCP 栈会根据其超时设置尝试重传消息。但是,如果客户端长时间无法恢复网络连接,服务端最终可能会因为超时过多而关闭连接。
总结来说,操作系统的 TCP/IP 栈确实会缓存未发送确认的消息,并在必要时进行重传,这是 TCP 协议保证数据传输可靠性的一部分。然而,WebSocket 应用层需要正确处理这些情况,以确保应用层的逻辑能够适应底层 TCP 协议的行为。