SpringBoot 实现WebSocket进行消息发送(适用于SpringMVC)

来源:互联网 发布:求免费数据恢复软件 编辑:程序博客网 时间:2024/06/05 17:27

Spring框架中自带了WebSocket的jar包,利用它可以实现与H5中WebSocket进行对接,实现交互。使用Spring WebSocket并不复杂,下面一起来看下怎么实现吧(注:本例子是通过SpringBoot构建的项目,除了项目的启动代码配置不一样外,WebSocket的配置代码可在SpringMVC上无缝使用)。

这个例子会通过模拟登陆,在Session中保存用户信息,然后进行WebSocket通信时,从Session中将用户信息取出来,并将信息转发给指定用户。

一、所需POM配置

SpringMVC配置:

<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-websocket</artifactId>    <version>4.3.10.RELEASE</version></dependency>

通过SpringMVC来构建项目的话,除了一些基本的Spring jar包之外,只需要添加上面这个jar包即可。

SpringBoot配置:

<build>    <plugins>      <plugin>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-maven-plugin</artifactId>      </plugin>    </plugins>  </build>  <parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>1.5.6.RELEASE</version>    <relativePath/>  </parent>  <dependencies>    <dependency>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter</artifactId>      </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-starter-thymeleaf</artifactId>      </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-websocket</artifactId>    </dependency>  </dependencies>

使用SpringBoot来构建项目的话,需要使用上面这些jar包,上面的jar包包含了SpringBoot的配置、WebSocket的配置。

二、代码实现

启动类

@SpringBootApplication@Controller@ComponentScan(basePackages={"com.test.spring.boot"})   //自定义自动扫描public class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}

上面代码是SpringBoot的一个项目启动类,执行main方法就可以启动服务,相关配置在这篇文章里不是重点,就不写出来了,感兴趣可以看这篇文章:SpringBoot+Thymeleaf整合开发实例。

这里需要注意的是 @ComponentScan(basePackages={"com.test.spring.boot"}),代表了扫描的包名。

访问Controller

@Controller@RequestMapping("/")public class HelloController {    private static final String SESSION_USER = "user";    @Autowired    private MyWebSocketHandler myWebSocketHandler;    // 访问跳转到websocket.html页面    @RequestMapping("/socket")    public String websocket() {        return "websocket";    }    // 模拟登陆操作,将用户信息存入Session    @RequestMapping("/login")    public @ResponseBody String login(UserBean userBean, HttpServletRequest request) {        System.out.println("========================== 开始登录 ===================");        System.out.println("userId="+userBean.getId());        System.out.println("userId="+userBean.getUsername());        System.out.println("userId="+userBean.getPhone());        request.getSession().setAttribute(SESSION_USER, userBean);        System.out.println("========================== 登录成功 ===================");        return "success";    }    // 普通操作,触发WebSocket通信    @RequestMapping("/send/message")    public @ResponseBody String sendMessage(HttpServletRequest request) {        UserBean userBean = (UserBean) request.getSession().getAttribute(SESSION_USER);        boolean isSuccess = myWebSocketHandler.sendMessageToUser(userBean.getId(), "测试发送消息");        System.out.println(isSuccess);        return "message";    }}

注册WebSocket处理类

@Configuration@EnableWebSocketpublic class WebSocketConfig implements WebSocketConfigurer {    @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        registry.addHandler(new MyWebSocketHandler(), "/websocket").addInterceptors(new WebSocketInterceptor());    }}

这是一个WebSocket配置类,实现接口来配置Websocket请求的路径和拦截器。 这里是将请求路径 /websocket绑定到MyWebSocketHandler()类,并设置一个拦截器WebSocketInterceptor()
注解不要忘掉,否则无法注册这个配置类。

WebSocket拦截器

/** * WebSocket 拦截器,用于将用户信息从session中存入map,方便后面websocket请求时从map中找到指定的用户session信息 * @author Administrator * */public class WebSocketInterceptor implements HandshakeInterceptor {    private static final String SESSION_USER = "user";    @Override    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,             WebSocketHandler wsHandler, Map<String, Object> map) throws Exception {        if (request instanceof ServletServerHttpRequest) {            ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;            HttpSession session = serverHttpRequest.getServletRequest().getSession();            if (session != null) {                map.put(SESSION_USER, session.getAttribute(SESSION_USER));            }        }        return true;    }    @Override    public void afterHandshake(ServerHttpRequest request,            ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {    }}

这个拦截器的作用是在请求WebSocket时,将Session中的用户信息转存到map中,方便后面WebSocket请求处理时,能够从map中获取到用户信息。

WebSocket具体请求处理类

在贴代码之前,我们先来看下WebSocket具体请求处理类中的那些方法可以重写,他们是什么含义:

  1. afterConnectionEstablished连接建立成功之后,记录用户的连接标识,便于后面发信息,这里我是记录将id记录在Map集合中。

  2. handleTextMessage中可以对H5 Websocket的send方法进行处理。

  3. handleTransportError连接出错处理,主要是关闭出错会话的连接,和删除在Map集合中的记录。

  4. afterConnectionClosed连接已关闭,移除在Map集合中的记录。

@Componentpublic class MyWebSocketHandler extends TextWebSocketHandler {    //在线用户列表    private static final Map<Integer, WebSocketSession> users = new HashMap<Integer, WebSocketSession>();    //用户标识    private static final String SESSION_USER = "user";    /**     * 连接已关闭,移除在Map集合中的记录     */    @Override    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {        users.remove(getUserId(session));    }    @Override    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {        if (session.isOpen()) {            session.close();        }        System.out.println("连接出错");        users.remove(getUserId(session));    }    /**     * 连接建立成功之后,记录用户的连接标识,便于后面发信息     */    @Override    public void afterConnectionEstablished(WebSocketSession session) throws Exception {        System.out.println("成功建立连接");        Integer userId = getUserId(session);        System.out.println(userId);        if (userId != null) {            users.put(userId, session);            session.sendMessage(new TextMessage("成功建立socket连接"));            System.out.println(userId);            System.out.println(session);        }    }    /**     * 处理收到的websocket信息     */    @Override    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {    }     /**     * 发送信息给指定用户     * @param clientId     * @param message     * @return     */    public boolean sendMessageToUser(Integer clientId, String message) {        if (users.get(clientId) == null) {            return false;        }        WebSocketSession session = users.get(clientId);        System.out.println("sendMessage:" + session);        if (!session.isOpen()) {            return false;        }        try {            int count = 1;            TextMessage textMessage = null;             String newMessage = "";            // 循环向客户端发送数据            while(true) {                newMessage = message + String.valueOf(count);                textMessage = new TextMessage(newMessage);                session.sendMessage(textMessage);                Thread.sleep(5000);                newMessage = "";            }        } catch (IOException e) {            e.printStackTrace();            return false;        } catch (InterruptedException e) {            e.printStackTrace();            return false;        }    }    /**     * 广播信息     * @param message     * @return     */    public boolean sendMessageToAllUsers(TextMessage message) {        boolean allSendSuccess = true;        Set<Integer> clientIds = users.keySet();        WebSocketSession session = null;        for (Integer clientId : clientIds) {            try {                session = users.get(clientId);                if (session.isOpen()) {                    session.sendMessage(message);                }            } catch (IOException e) {                e.printStackTrace();                allSendSuccess = false;            }        }        return  allSendSuccess;    }     /**     * 获取用户标识     * @param session     * @return     */    private Integer getUserId(WebSocketSession session) {        try {            UserBean userBean = (UserBean) session.getAttributes().get(SESSION_USER);            return userBean.getId();        } catch (Exception e) {            return null;        }    }}

前端实现

<!DOCTYPE HTML><html xmlns:th="http://www.thymeleaf.org"><head><title>WebSocket Test</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><link rel="stylesheet" th:href="@{/css/main.css}"/><script th:src="@{/js/jQuery-2.1.4.min.js}" type="text/javascript"></script></head><body>    <div>        <input type="button" id="btn_test" value="点击获取数据"/>    </div>    <div>        <div id="test_1">        </div>    </div></body><script type="text/javascript">//<![CDATA[ $(document).ready(function() {    var websocket =  null;    var sendData = {"id": 1, "username": "tanjin", "phone": "1888221322122"};    // 模拟登陆    $.post("login", sendData, function(data) {        // 初始化一个WebSocket连接        websocket = new WebSocket("ws://localhost:8082/websocket");        websocket.onerror = function(event) {onError(event);};        websocket.onopen = function(event) {onOpen(event);};        // websocket接收的信息会通过这个回调方法返回给前端        websocket.onmessage = function(event) {onMessage(event);};    })    // 打印收到的websocket返回信息    function onMessage(event) {        $("#test_1").append("<label class='navbar-label'>" + event.data + "</label>").append("</br>");        $("#test_1").scrollTop( $("#test_1")[0].scrollHeight);    }    // 开启websocket请求回调处理    function onOpen(event) {        $("#test_1").empty();        var label = $("<label>").html("开始执行....");        $("#test_1").append(label).append("<br>").append("<br>");    }    //报错处理回调函数    function onError(event) {        alert(event.data);    }    //点击页面上的一个按钮,通过一个普通方法来开始一个websocket请求    $("#btn_test").click(function() {        $.post("send/message", null, function() {})    })})//]]></script></html>

//<![CDATA[ 这个标签在这里是由于thymeleaf的语法限制使用的,如果是JSP则不需要。

处理结果

这里写图片描述

阅读全文
1 0
原创粉丝点击