Spring集成Websocket环境


Spring集成Websocket环境

对于 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 ”:

Spring集成Websocket环境-打不死的小强

点击“确定”,看到“数据发送中…”提示,点击“确定”:

Spring集成Websocket环境-打不死的小强

可以看到收到了来自服务端的消息:

Spring集成Websocket环境-打不死的小强

同时,在后端控制台查看输出,

Spring集成Websocket环境-打不死的小强

OK,测试成功。

说明,上面只是个 Demo,要根据实际需求进行完整的业务逻辑代码编写,如保存客户端信息,处理客户端请求,给客户端发送数据等。



发表评论

电子邮件地址不会被公开。 必填项已用*标注