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

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
@EnableWebSocket
public 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集合中的记录。

@Component
public 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
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值