对于 web 应用程序,WebSocket 协议RFC 6455定义了一个很重要的功能:全双工,客户端与服务器之间的双向通信.
简单说来,Websocket 初始握手依赖于 HTTP,客户端通过 HTTP 发出请求协议进行升级,服务器可以使用 HTTP 状态 101 对其进行响应同意,握手成功后,HTTP 升级请求下面的 TCP 套接字保持打开,客户端和服务器都可以使用它来彼此发送消息。
Spring 在 4.0 后将 websocket 集成了进去,即 spring-websocket 模块。它与 Java WebSocket API 标准(JSR-356)兼容,并且还提供额外功能。
下面来看 Spring 集成 Websocket 的过程:
1、添加 spring-websocket 依赖
如果是 Maven 项目,则:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.1.3.RELEASE</version> </dependency>
如果是 Gradle 项目,则:
compile group: 'org.springframework', name: 'spring-websocket', version: '5.1.3.RELEASE'
记得更新项目
2、Websocket 处理类
自定义 WebsocketHandler 类,可以 implements Spring 的 WebSocketHandler,或者 extends TextWebSocketHandler 甚至 BinaryWebSocketHandler,这里 extends TextWebSocketHandler。用于进行建立连接,关闭连接,接收到客户端消息的处理等。
public class WsHandler extends TextWebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { //建立连接后的操作 super.afterConnectionEstablished(session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { //收到消息后的操作 super.handleMessage(session, message); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { //连接出错后的操作 super.handleTransportError(session, exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { //连接关闭后的操作 super.afterConnectionClosed(session, closeStatus); } }
3、Websocket server 配置类
自定义配置类implements WebSocketConfigurer,如下采用 java 配置类方式,通过添加@Configuration 和@EnableWebSocket 注解。用于注册处理类,URL 映射,添加握手拦截器,配置允许的域名,sockjs 的支持等。
@Configuration @EnableWebSocket public class WsConfig implements WebSocketConfigurer{ @Autowired private WsHandshakeInterceptor wsHandshakeInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler(), "/wsHandler") //注册 handler,这里的 url 要与页面的 url 一致 .setAllowedOrigins("*") // 允许请求的域名 .addInterceptors(wsHandshakeInterceptor); //添加握手拦截 //旧版浏览器可能不支持 websocket,通过 sockjs 模拟 websocket 的行为,所以下面要配 sockjs 支持。 registry.addHandler(wsHandler(), "/sockjs").setAllowedOrigins("*") .addInterceptors(wsHandshakeInterceptor).withSockJS(); } @Bean public WebSocketHandler wsHandler(){ return new WsHandler(); } }
说明:有关各代码的作用都已在代码行注解说明。上面添加了握手拦截 addInterceptors(wsHandshakeInterceptor),下一步讲。
4、websocket 握手拦截
自定义我说拦截器,通过 extends HttpSessionHandshakeInterceptor,用于建立连接前的握手拦截处理,如拦截非法连接,保存连接客户端信息等。
@Component public class WsHandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { String token = ((ServletServerHttpRequest) request).getServletRequest().getParameter(PropertyConfigurer.getProperty("ws.client.key")); //握手前的操作 return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { //握手后的操作 super.afterHandshake(request, response, wsHandler, exception); } }
完成了上面 4 步,就可以接受 ws 客户端连接了。
5、测试连接
如下为 js 客户端连接代码,文件名:ws.html
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>webSocket 入门示例</title> <script type="text/javascript"> function WebSocketTest() { if ("WebSocket" in window) { alert("您的浏览器支持 WebSocket!"); // 打开一个 web socket 连接 var ws = new WebSocket("ws://localhost:8080/hiov/wsHandler?connector=lwq"); // Web Socket 已连接上,使用 send() 方法发送数据 ws.onopen = function() { ws.send("发送数据"); alert("数据发送中..."); }; //接收数据后 ws.onmessage = function (evt) { var received_msg = evt.data; alert(received_msg); alert("数据已接收..."); }; // 关闭 websocket 后 ws.onclose = function() { alert("连接已关闭..."); }; } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } } </script> </head> <body> <div id="sse"> <a href="javascript:WebSocketTest()">运行 WebSocket</a> </div> </body> </html>
修改 WsHandshakeInterceptor 握手前代码
@Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { String connector= ((ServletServerHttpRequest) request).getServletRequest().getParameter("connector"); if (connector == null || connector.equals("")) { System.out.println("拒绝连接"); return false; } else { attributes.put("connector", connector); return true; } }
修改 WsHandler 连接后和收到消息后代码
@Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { String connector = (String)session.getAttributes().get("connector"); System.out.println("已连接:"+ connector); super.afterConnectionEstablished(session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { System.out.println("收到消息:" + message.getPayload()); session.sendMessage(new TextMessage("收到了,谢谢!")); //给客户端发送数据 super.handleMessage(session, message); }
启动项目,浏览器打开 ws.html,点击“ 运行 WebSocket ”:
点击“确定”,看到“数据发送中…”提示,点击“确定”:
可以看到收到了来自服务端的消息:
同时,在后端控制台查看输出,
OK,测试成功。
说明,上面只是个 Demo,要根据实际需求进行完整的业务逻辑代码编写,如保存客户端信息,处理客户端请求,给客户端发送数据等。