SpringMVC使用websocket做消息推送

WebSocket

WebSocket协议支持(在受控环境中运行不受信任的代码的)客户端与(选择加入该代码的通信的)远程主机之间进行全双工通信。用于此的安全模型是Web浏览器常用的基于原始的安全模式。 协议包括一个开放的握手以及随后的TCP层上的消息帧。 该技术的目标是为基于浏览器的、需要和服务器进行双向通信的(服务器不能依赖于打开多个HTTP连接(例如,使用XMLHttpRequest或<iframe>和长轮询))应用程序提供一种通信机制。

socket消息推送流程

  1. 后台创建socket服务 ;
  2. 用户登录后与后台建立socket连接,默认使用websocket,如果浏览器不支持则使用scokjs连接;
  3. 建立连接后,服务端可以向用户推送信息;

javaweb中,socket的实现方式有多种,这里使用Spring-webscoket的方式实现。

demo

搭建环境

在SpringMVC的项目基础上,导入websocket的相关jar包。

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

websocket服务端实现类


@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{

    private static final Logger logger = LoggerFactory.getLogger(WebSocketConfig.class);
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        System.out.println("==========================注册socket");
        //注册websocket server实现类,"/webSocketServer"访问websocket的地址
        registry.addHandler(msgSocketHandle(),
                "/webSocketServer").
                addInterceptors(new WebSocketHandshakeInterceptor());
        //使用socketjs的注册方法
        registry.addHandler(msgSocketHandle(),
                "/sockjs/webSocketServer").
                addInterceptors(new WebSocketHandshakeInterceptor())
                .withSockJS();
    }

     /**
     *
     * @return 消息发送的Bean
     */
    @Bean(name = "msgSocketHandle")
    public WebSocketHandler msgSocketHandle(){
        return new MsgScoketHandle();
    }
}

这里使用的config配置的形式注册bean和配置,所以需要在SpringMVC的配置文件中添加对类的自动扫描

    <mvc:annotation-driven />
    <context:component-scan base-package="com.wqh.websocket"/>

拦截器类

主要是获取到当前连接的用户,并把用户保存到WebSocketSession中

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
   private static final Logger logger = LoggerFactory.getLogger(WebSocketHandshakeInterceptor.class);

    /**
     * 握手前
     * @param request
     * @param response
     * @param webSocketHandler
     * @param attributes
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler webSocketHandler, Map<String, Object> attributes) throws Exception {
        logger.info("握手操作");
        if (request instanceof ServletServerHttpRequest){
           ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
           HttpSession session = servletServerHttpRequest.getServletRequest().getSession(false);
           if(session != null){
                //从session中获取当前用户
               User user = (User) session.getAttribute("user");
               attributes.put("user",user);
           }
       }

        return true;
    }

    /**
     * 握手后
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {

    }
}

socket处理消息类

@Component
public class MsgScoketHandle implements WebSocketHandler {

    /**已经连接的用户*/
    private static final ArrayList<WebSocketSession> users;

    static {
        //保存当前连接用户
        users = Lists.newArrayList();
    }

    /**
     * 建立链接
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        //将用户信息添加到list中
        users.add(webSocketSession);
        System.out.println("=====================建立连接成功==========================");
        User user  = (User) webSocketSession.getAttributes().get("user");
        if(user != null){
            System.out.println("当前连接用户======"+user.getName());
        }
        System.out.println("webSocket连接数量====="+users.size());
    }

    /**
     * 接收消息
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
        User user = (User) webSocketSession.getAttributes().get("user");
        System.out.println("收到用户:"+user.getName()+"的消息");
        System.out.println(webSocketMessage.getPayload().toString());
        System.out.println("===========================================");

    }

    /**
     * 异常处理
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable){
        if (webSocketSession.isOpen()){
            //关闭session
            try {
                webSocketSession.close();
            } catch (IOException e) {
            }
        }
        //移除用户
        users.remove(webSocketSession);
    }

    /**
     * 断开链接
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        users.remove(webSocketSession);
        User user = (User) webSocketSession.getAttributes().get("user");
        System.out.println(user.getName()+"断开连接");
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 发送消息给指定的用户
     * @param user
     * @param messageInfo
     */
    public void sendMessageToUser(User user, TextMessage messageInfo){
        for (WebSocketSession session : users) {
            User sessionUser = (User) session.getAttributes().get("user");
            //根据用户名去判断用户接收消息的用户
            if(user.getName().equals(sessionUser.getName())){
                try {
                    if (session.isOpen()){
                        session.sendMessage(messageInfo);
                        System.out.println("发送消息给:"+user.getName()+"内容:"+messageInfo);
                    }
                    break;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

controller及页面

这里简单的模拟登录,前台传入登录参数,直接将参数保存到session中。

@RequestMapping("websocket")
@Controller
public class UserController {

    @Autowired
    private MsgScoketHandle msgScoketHandle;

    @RequestMapping("login")
    public String login(User user, HttpServletRequest request){
        user.setId(UUID.randomUUID().toString().replace("-",""));
        request.getSession().setAttribute("user",user);
        return "/index";
    }

    @ResponseBody
    @RequestMapping("sendMsg")
    public String sendMag(String content,String toUserName){
        User user = new User();
        user.setName(toUserName);
        TextMessage textMessage = new TextMessage(content);
        msgScoketHandle.sendMessageToUser(user,textMessage);
        return "200";
    }
}

登录页面省略,直接socket连接页面,这里使用sockjs来创建连接,所以需要先添加js文件
sockjs.min.js

<script>
    $(document).ready(function() {
        var ws;
        if ('WebSocket' in window) {
            ws = new WebSocket("ws://"+window.location.host+"/webSocketServer");
        } else if ('MozWebSocket' in window) {
            ws = new MozWebSocket("ws://"+window.location.host+"/webSocketServer");
        } else {
            //如果是低版本的浏览器,则用SockJS这个对象,对应了后台“sockjs/webSocketServer”这个注册器,
            //它就是用来兼容低版本浏览器的
            ws = new SockJS("http://"+window.location.host+"/sockjs/webSocketServer");
        }
        ws.onopen = function (evnt) {
        };
        //接收到消息
        ws.onmessage = function (evnt) {
            alert(evnt.data);
            $("#msg").html(evnt.data);
        };
        ws.onerror = function (evnt) {
            console.log(evnt)
        };
        ws.onclose = function (evnt) {
        }

        $("#btn1").click(function () {

            ws.send($("#text").val());
        });
        $("#btn2").bind("click",function () {
            var url = "${pageContext.request.contextPath}/websocket/sendMsg";
            var content =  $("#text").val();
            var toUserName = "admin"
            $.ajax({
                data: "content=" + content + "&toUserName=" + toUserName,
                type: "get",
                dataType: 'text',
                async: false,
                contentType: "application/x-www-form-urlencoded;charset=UTF-8",
                encoding: "UTF-8",
                url: url,
                success: function (data) {
                    alert(data.toString());
                },
                error: function (msg) {
                    alert(msg);
                },

            });

        })
    });

</script>
<body>
当前登录用户:${pageContext.session.getAttribute("user").name}<br>
    <input type="text" id="text">
    <button id="btn1" value="发送给后台">发送给后台</button>
    <button id="btn2" value="发送给其他用户">发送给其他用户</button>
    <div id="msg"></div>
</body>
</html>

启动项目

在控制可以看到socket注册成功

访问页面,第一个用户使用admin登录,第二个使用1234登录

首先将消息发送给后台,后台打印消息

使用1234用户发送消息给admin

爬坑

在Springmvc项目中都会指定连接访问的后缀,比如.do、.action,但是这里会导致按照以上配置会导致前端连接socket服务时404。我的解决办法是修改web.xml,将DispatcherServlet<url-pattern>改为/。。。但是新的问题又出现了,页面无法加载资源文件,所以还需要在SpringMVC.xml中添加对静态资源的配置,这里具体的mappinglocation看自己的具体项目。

    <mvc:resources mapping="/css/**" location="/css/" />
    <mvc:resources mapping="/images/**" location="/images/" />
    <mvc:resources mapping="/js/**" location="/js/" />
  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: SpringMVC整合WebSocket可以通过以下步骤实现: 1. 添加WebSocket依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>5.3.9</version> </dependency> ``` 2. 配置WebSocketSpringMVC配置文件中添加以下配置: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new MyWebSocketHandler(), "/my-websocket"); } } ``` 其中,MyWebSocketHandler是自定义的WebSocket处理器,"/my-websocket"是WebSocket的访问路径。 3. 编写WebSocket处理器 自定义的WebSocket处理器需要实现WebSocketHandler接口,例如: ``` public class MyWebSocketHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // 连接建立后的处理逻辑 } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { // 处理收到的消息 } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { // 处理传输错误 } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { // 连接关闭后的处理逻辑 } @Override public boolean supportsPartialMessages() { return false; } } ``` 4. 编写WebSocket客户端 可以使用JavaScript或其他语言编写WebSocket客户端,例如: ``` var socket = new WebSocket("ws://localhost:8080/my-websocket"); socket.onopen = function() { // 连接建立后的处理逻辑 }; socket.onmessage = function(event) { // 处理收到的消息 }; socket.onclose = function(event) { // 连接关闭后的处理逻辑 }; ``` 以上就是SpringMVC整合WebSocket的基本步骤。 ### 回答2: Spring MVC 是一个基于 Java 的 Web 框架,可以将 WebSocket 整合到 Spring MVC 中,实现实时通信的功能。下面详细介绍 Spring MVC 整合 WebSocket 的步骤: 1. 配置 Maven 依赖:在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.3.13.RELEASE</version> </dependency> ``` 2. 配置 WebSocket 消息处理器:创建一个类来处理 WebSocket 消息。该类需要实现 `WebSocketHandler` 接口,实现处理 WebSocket 消息的方法。 ``` public class MyWebSocketHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // WebSocket 连接建立时执行的操作 } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { // 处理收到的 WebSocket 消息 } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { // 处理 WebSocket 消息传输过程中出现的异常 } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { // WebSocket 连接关闭时执行的操作 } @Override public boolean supportsPartialMessages() { return false; } } ``` 3. 配置 Spring MVC WebSocket 拦截器:创建一个类,实现 `HandshakeInterceptor` 接口。该拦截器可在 WebSocket 握手阶段执行特定的操作,例如在 WebSocket 握手时将用户信息添加到 session 中。 ``` public class MyWebSocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // 在 WebSocket 握手之前执行的操作 return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, @Nullable Exception exception) { // 当 WebSocket 握手成功时执行的操作 } } ``` 4. 配置 Spring MVC WebSocket:在 Spring MVC 配置文件中配置 WebSocket 服务。可以使用 `WebSocketHandlerRegistry` 注册 WebSocket 消息处理器,也可以使用 `HandshakeInterceptorRegistry` 注册 WebSocket 拦截器。 ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private MyWebSocketHandler myWebSocketHandler; @Autowired private MyWebSocketInterceptor myWebSocketInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler, "/myWebSocket") .addInterceptors(myWebSocketInterceptor) .setAllowedOrigins("*"); } } ``` 5. 创建 WebSocket 客户端:在客户端中可以使用 JavaScript 代码调用 WebSocket 服务。使用 `var ws = new WebSocket("ws://localhost:8080/myWebSocket");` 创建 WebSocket 连接,然后可以监听 `ws.onopen`、`ws.onerror`、`ws.onmessage` 和 `ws.onclose` 等事件,并在这些事件发生时执行相应的操作。 总结来说,Spring MVC 整合 WebSocket 的步骤包括配置 Maven 依赖、配置 WebSocket 消息处理器、配置 WebSocket 拦截器、配置 Spring MVC WebSocket 和创建 WebSocket 客户端等。使用 Spring MVC 整合 WebSocket 可以使得在 Web 应用中实现实时通信变得更加简单和方便。 ### 回答3: Spring MVC 是一个基于 MVC(Model-View-Controller) 模式的 WEB 应用程序开发框架,而 WebSocket 是 HTML5 提出的基于 TCP 协议的全双工通讯技术,它可以让浏览器与服务器进行实时的双向数据传输。为了实现 WebsocketSpring MVC 的整合,我们需要通过以下步骤: 1. 添加 Spring WebSocket 依赖 首先在 Maven 或 Gradle 项目的 `pom.xml` 或 `build.gradle` 文件中添加 Spring WebSocket 的依赖,如果是手动添加 jar 包,则需要添加 `spring-websocket.jar` 和 `javax-websocket-client-api.jar` ,并且需要确保其版本和 Spring 的版本匹配。 2. 配置 WebSocket 处理器 接下来我们需要为 WebSocket 客户端和服务端分别配置其处理器。 在服务端,我们需要创建一个类来处理 WebSocket 连接请求,该类需要继承 `WebSocketHandler` 接口并实现其中的 `handleMessage`、`handleTransportError` 和 `afterConnectionClosed` 方法。 然后将这个类注册到 `WebSocketHandlerRegistry` 中,代码如下: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new MyWebSocketHandler(), "/myHandler").setAllowedOrigins("*"); } } public class MyWebSocketHandler extends TextWebSocketHandler { @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException { // 处理收到的消息 } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { // 处理传输错误 } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { // 处理关闭连接事件 } } ``` 在上述代码中,我们创建了一个 `WebSocketConfig` 配置类,并使用 `@EnableWebSocket` 注解开启 WebSocket 支持。 然后在 `registerWebSocketHandlers` 方法中,我们注册了一个 `MyWebSocketHandler` 处理器,并为其指定了路径 `"/myHandler"`。需要注意的是,我们使用 `setAllowedOrigins("*")` 方法指定了跨域访问的规则。 3. 在客户端发起连接 客户端连接 WebSocket 时需要通过标准的 WebSocket API 建立连接。这里我们以 JavaScript 代码为例: ``` var ws = new WebSocket("ws://localhost:8080/myHandler"); ws.onopen = function() { console.log("连接成功"); }; ws.onmessage = function(event) { console.log("收到消息:" + event.data); } ws.onclose = function() { console.log("连接关闭"); } ``` 在上述代码中,我们首先创建了一个 WebSocket 对象,并指定连接的 URL 为 `ws://localhost:8080/myHandler`。然后通过 `onopen`、`onmessage` 和 `onclose` 等事件监听器来处理连接成功、接收消息和连接关闭事件。 通过以上步骤,我们就完成了 WebSocketSpring MVC 的整合。在实际的应用中,我们可以根据自己的需要来进一步扩展和优化代码,以达到更好的性能和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值