websocket关闭指定原因
如果没有宽恕之心,生命会被无休止的仇恨和报复所支配。——阿萨吉奥
WebSocket 是一种轻量级、双向的实时通信协议,在现代 Web 应用中非常流行。它为客户端和服务端提供了长连接能力,适用于需要频繁数据交互的场景。然而,在实际开发中,我们经常需要处理 WebSocket 的关闭事件,而 关闭状态(CloseStatus) 是其中一个重要的概念,它能够帮助开发者理解连接关闭的原因,从而采取相应的措施。
什么是 CloseStatus?
在 WebSocket 协议中,每次连接关闭都会携带一个 关闭码(close code) 和可选的 关闭原因(reason phrase)。这些关闭码由 RFC 6455 定义,表示连接关闭的原因。例如:
- 1000 (Normal Closure): 正常关闭,表示连接完成。
- 1001 (Going Away): 客户端或服务端主动断开(例如页面关闭)。
- 1002 (Protocol Error): 协议错误。
- 1003 (Unsupported Data): 不支持的数据类型。
在 Spring Framework 中,org.springframework.web.socket.CloseStatus 提供了对这些状态的封装,便于我们处理 WebSocket 关闭事件。
1 |
|
Spring WebSocket 中的 CloseStatus
Spring 提供了 CloseStatus 类来封装关闭码和原因。以下是 CloseStatus 的关键方法和属性:
getCode(): 获取关闭码。getReason(): 获取关闭的原因(可能为空)。- 常量值: Spring 提供了常见关闭状态的预定义常量,例如
CloseStatus.NORMAL和CloseStatus.PROTOCOL_ERROR。
1 | // 使用 CloseStatus 处理关闭事件 |
应用场景:处理不同的 CloseStatus
正常关闭 (1000)
适用于连接完成或用户主动断开。可以在关闭事件中释放资源、关闭相关线程或记录日志。异常关闭 (1006)
常见于网络问题或客户端断开。可以设置重连机制来保持连接的稳定性。协议错误 (1002)
当客户端发送了不符合协议的数据时,服务端可以选择断开连接。此时应在日志中记录详细信息,方便排查问题。服务器繁忙 (1013)
如果服务端压力过大,可以选择发送此关闭状态,让客户端稍后重试。
1 |
|
客户端处理关闭事件
在客户端中,我们也可以捕获 onclose 事件,并基于关闭状态码进行不同的操作。例如:
1 | const socket = new WebSocket("wss://example.com/socket"); |
常见问题与最佳实践
1. 为什么会收到 1006 状态?
1006 是由客户端生成的关闭码,通常用于无法与服务端正常通信的场景(例如网络中断)。建议在服务端日志中查看异常原因。
2. 如何向客户端发送自定义关闭状态?
Spring 提供了 WebSocketSession.close(CloseStatus) 方法,可以指定关闭码和原因。
1 | session.close(new CloseStatus(4001, "自定义错误: Token 无效")); |
客户端会在 onclose 事件中接收到此信息。
3. 如何避免意外关闭?
- 定期发送心跳(ping/pong)以保持连接活跃。
- 在连接关闭后实现自动重连。
- 在关闭前提示用户保存未完成的数据。
状态码一览:
1000 - NORMAL
含义: 连接正常关闭,表明 WebSocket 通信已完成。
应用场景: 客户端或服务端主动关闭连接,释放资源。
示例:
1
session.close(CloseStatus.NORMAL);
1001 - GOING_AWAY
含义: 连接关闭是由于某一方离开,例如服务器关闭或浏览器跳转页面。
应用场景: 服务器维护期间关闭连接,或者用户关闭浏览器窗口。
示例:
1
session.close(CloseStatus.GOING_AWAY);
1002 - PROTOCOL_ERROR
含义: 由于协议错误而关闭连接。
应用场景: 客户端或服务端未遵循 WebSocket 协议(例如发送非法帧)。
示例:
1
session.close(CloseStatus.PROTOCOL_ERROR);
1003 - NOT_ACCEPTABLE
含义: 收到了无法处理的数据类型(例如服务端只接受文本,但收到了二进制消息)。
应用场景: 数据类型不匹配时关闭连接。
示例:
1
session.close(CloseStatus.NOT_ACCEPTABLE);
1005 - NO_STATUS_CODE
- 含义: 未提供状态码的关闭,保留值。
- 应用场景: 一般用于表示关闭帧中没有状态码,不能直接使用。
1006 - NO_CLOSE_FRAME
含义: 连接非正常关闭,例如未发送关闭帧。
应用场景: 网络中断、客户端或服务端崩溃等。
注意: 此状态码仅在客户端或工具中报告,不会出现在关闭帧中。
示例:
1
2
3if (status.equals(CloseStatus.NO_CLOSE_FRAME)) {
// 记录异常并尝试重连
}
1007 - BAD_DATA
含义: 收到了与消息类型不一致的数据(例如,非 UTF-8 数据)。
应用场景: 数据格式验证失败时关闭连接。
示例:
1
session.close(CloseStatus.BAD_DATA);
1008 - POLICY_VIOLATION
含义: 收到的消息违反了服务器的策略。
应用场景: 服务器限制了某些操作或内容(例如,未授权访问)。
示例:
1
session.close(CloseStatus.POLICY_VIOLATION.withReason("Unauthorized access"));
1009 - TOO_BIG_TO_PROCESS
含义: 收到的消息太大,无法处理。
应用场景: 限制消息大小的服务器可能在超出限制时关闭连接。
示例:
1
session.close(CloseStatus.TOO_BIG_TO_PROCESS);
1010 - REQUIRED_EXTENSION
含义: 客户端期望服务器支持某些扩展,但服务器未提供。
应用场景: 客户端无法与服务器达成握手协议。
示例:
1
session.close(CloseStatus.REQUIRED_EXTENSION.withReason("Missing compression extension"));
1011 - SERVER_ERROR
含义: 服务器由于内部错误无法处理请求。
应用场景: 服务器发生未知异常时关闭连接。
示例:
1
session.close(CloseStatus.SERVER_ERROR.withReason("Unexpected internal error"));
1012 - SERVICE_RESTARTED
含义: 服务端正在重启,客户端可以稍后重连。
应用场景: 定期维护或部署新版本时关闭连接。
示例:
1
session.close(CloseStatus.SERVICE_RESTARTED);
1013 - SERVICE_OVERLOAD
含义: 服务端过载,建议客户端切换到其他服务器或稍后再试。
应用场景: 服务器资源不足时主动关闭连接。
示例:
1
session.close(CloseStatus.SERVICE_OVERLOAD);
1015 - TLS_HANDSHAKE_FAILURE
- 含义: TLS 握手失败,保留值。
- 应用场景: 用于标记安全连接建立失败的情况。
扩展状态码
4500 - SESSION_NOT_RELIABLE
含义: 会话变得不可靠,例如在超时发送消息时。
应用场景: 服务器检测到会话不稳定时可主动关闭连接。
示例:
1
session.close(CloseStatus.SESSION_NOT_RELIABLE);
