java

spring集成websocket

Posted on 2021-03-25,8 min read

maven依赖

        <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-websocket</artifactId>
              <version>4.0.3.RELEASE</version>
        </dependency>
        <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-messaging</artifactId>
              <version>4.3.2.RELEASE</version>
        </dependency>
        <dependency>
              <groupId>javax.websocket</groupId>
              <artifactId>javax.websocket-api</artifactId>
              <version>1.0</version>
              <scope>provided</scope>
        </dependency>
        <dependency>
              <groupId>org.eclipse.jetty.websocket</groupId>
              <artifactId>websocket-server</artifactId>
              <version>9.3.3.v20150827</version>
              <scope>provided</scope>
        </dependency>
        <dependency>
              <groupId>org.apache.tomcat.embed</groupId>
              <artifactId>tomcat-embed-websocket</artifactId>
              <version>8.0.23</version>
              <scope>provided</scope>
        </dependency>

配置入口类

配置WebSocket的入口,编写WebSocketConfig类实现WebSocketConfigurer 接口

package com.hiwei.demo.webSocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
@Configuration
@EnableWebSocket
@EnableWebMvc
public class SpringWebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler(),"/websocket/socketServer.do").addInterceptors(new SpringWebSocketHandlerInterceptor());
        registry.addHandler(webSocketHandler(), "/sockjs/socketServer.do").addInterceptors(new SpringWebSocketHandlerInterceptor()).withSockJS();      
    }
    @Bean
    public TextWebSocketHandler webSocketHandler(){
        return new SpringWebSocketHandler();
    }
}

定义入口类

定义一个SpringWebSocketHandler类继承TextWebSocketHandler,这个类是用来处理Websocket连接建立、断开,消息发送的逻辑的,这个是消息处理的核心代码:

package com.hiwei.demo.webSocket;
import java.io.IOException;
import java.util.HashMap;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class SpringWebSocketHandler extends TextWebSocketHandler {
 
    private static final HashMap<String,WebSocketSession> users;
    static {
        users = new HashMap<String,WebSocketSession>();
    }
   
    public SpringWebSocketHandler() {
    }
   
    /**
     * 连接成功触发
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        users.put((String) session.getAttributes().get("SESSION_USERID"), session);
        System.out.println("connect to the websocket success......当前数量:"+users.size());
        //这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户
      TextMessage returnMessage = new TextMessage("连接成功");
       session.sendMessage(returnMessage);
    }
    /**
     * 关闭连接时触发
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME");
        System.out.println("用户"+username+"已退出!");
        users.remove(session.getAttributes().get("SESSION_USERID"));
        System.out.println("剩余在线用户"+users.size());
    }
    /**
     * js调用websocket.send时候,会调用该方法
     */
    @Override   
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        TextMessage returnMessage = new TextMessage(message.getPayload().toString());
        sendMessageToUsers(returnMessage);
    }
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if(session.isOpen()){session.close();}
        users.remove(session.getAttributes().get("SESSION_USERID"));
    }
    public boolean supportsPartialMessages() {
        return false;
    }
    /**
     * 给某个用户发送消息
     *
     * @param userName
     * @param message
     */
    public void sendMessageToUser(String userName, TextMessage message) {
      for (WebSocketSession user: users.values()) {
            if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
            }
    }
    /**
     * 给所有在线用户发送消息
     *
     * @param message
     */
    public void sendMessageToUsers(TextMessage message) {
        for (WebSocketSession user : users.values()) {
            try {
                if (user.isOpen()) {
                    user.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } 
}

拦截器配置

从WebSocketConfig中可以看到在注册WebSocket通道时,不仅设置了入口地址,还配置了拦截器,拦截器可以实现握手之前和之后的逻辑操作,这里配置的拦截器主要用于保存用户名以便于在Handler中定向发送消息。

package com.hiwei.demo.webSocket;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                //使用userName区分WebSocketHandler,以便定向发送消息
                String userName = (String) session.getAttribute("SESSION_USERNAME");
                if (userName==null) {
                    userName="default-system";
                }
                attributes.put("WEBSOCKET_USERNAME",userName);
            }
        }
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception ex) {
        super.afterHandshake(request, response, wsHandler, ex);
    }
}

配置Websocket连接前台页面

网页端连接Websocket和推送消息的界面就是这里

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>消息</title>
</head>
<body>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script>
<script type="text/javascript">
    var msgDiv = document.getElementById("#msgDiv");
    var websocket = null;
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://172.30.99.37:8080/springmvcMybatis/websocket/socketServer.do");
    }
    else if ('MozWebSocket' in window) {
        websocket = new MozWebSocket("ws://172.30.99.37:8080/springmvcMybatis/websocket/socketServer.do");
    }
    else {
        websocket = new SockJS("http://172.30.99.37:8080/springmvcMybatis/sockjs/socketServer.do");
    }
    websocket.onopen = onOpen;     
    websocket.onmessage = onMessage;
    websocket.onerror = onError;
    websocket.onclose = onClose;
    function onOpen(openEvt) {
        //alert(openEvt.Data+"onOpen");
    }
    function onMessage(evt) {
            $('#msgDiv').val(evt.data)
       
            
    }
    function onError() {
        alert("出错"+"onError");
    }
    function onClose() {
        alert("关闭"+"onClose");
    }
    function doSend() {
        if (websocket.readyState == websocket.OPEN) {         
            var msg = document.getElementById("inputMsg").value; 
            websocket.send(msg);
        } else { 
            alert("连接失败!"); 
        } 
    }
    window.close=function(){
        websocket.onclose();
    }
</script>
<body align="center">
    <h3>消息推送</h3>
    请输入:<textarea rows="8" cols="50" id="inputMsg" name="inputMsg"></textarea>
    <button onclick="doSend();">发送</button>
    <hr/>
    <textarea rows="10" cols="70" id="msgDiv"></textarea>
</body>
</html>

另:web.xml中的servlet和filter中添加异步支持

<async-supported>true</async-supported>

下一篇: mysql索引→