波神

导航

springboot+websocket示例

1、新建maven工程

工程结构如下:

完整的pom.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.websocket.demo</groupId>
    <artifactId>websocket-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>
    <dependencies>
        <!-- thymeleaf 模板的配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring websocket的配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.4.5.RELEASE</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2、application.properties 的配置如下

#thymeleaf start
#spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
#开发时关闭缓存,不然没法看到实时页面
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
#thymeleaf end


server.port=8082

3、启动类

package com.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Administrator
 * @date 2018/08/18
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

 

4、WebsocketConfig 类进行了websocket的配置

package com.websocket.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

/**
 * @author hzb
 * @date 2018/09/30
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //启用/userTest,/topicTest,两个消息前缀
        config.enableSimpleBroker("/userTest","/topicTest");
        //如果不设置下面这一句,用convertAndSendToUser来发送消息,前端订阅只能用/user开头。
        config.setUserDestinationPrefix("/userTest");
        //客户端(html等)向服务端发送消息的前缀
        config.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //客户端和服务端进行连接的endpoint
        stompEndpointRegistry.addEndpoint("/websocket-endpoint").setAllowedOrigins("*").withSockJS();
    }
}

 

5、消息管理实现类

package com.websocket.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author hzb
 * @date 2018/09/30
 */
@Controller
@EnableScheduling
public class WebsocketMsgController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @GetMapping("/")
    public String index() {
        return "index";
    }

    /**
     * index.html将message发送给后端,后端再将消息重组后发送到/topicTest/web-to-server-to-web
     * @param message
     * @return
     * @throws Exception
     */
    @MessageMapping("/send")
    @SendTo("/topicTest/web-to-server-to-web")
    public String send(String message) throws Exception {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return "服务器将原消息返回: "+df.format(new Date())+" :" + message;
    }

    /**
     * 最基本的服务器端主动推送消息给前端
     * @return
     * @throws Exception
     */
    @Scheduled(fixedRate = 1000)
    public String serverTime() throws Exception {
        // 发现消息
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        messagingTemplate.convertAndSend("/topicTest/servertime", df.format(new Date()));
        return "servertime";
    }

    /**
     * 以下面这种方式发送消息,前端订阅消息的方式为: stompClient.subscribe('/userTest/hzb/info' 
     * @return
     * @throws Exception
     */
    @Scheduled(fixedRate = 1000)
    public String serverTimeToUser() throws Exception {
        // 发现消息
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //这里虽然没有指定发送前缀为/userTest,但是在WebsocketConfig.java中设置了config.setUserDestinationPrefix("/userTest"),
        //否则默认为/user
        messagingTemplate.convertAndSendToUser("hzb","/info", df.format(new Date()));
        return "serverTimeToUser";
    }
}

 

前端代码:index.html

<!DOCTYPE html>
<html>
<head>
    <title>玩转spring boot——websocket</title>
    <script src="//cdn.bootcss.com/angular.js/1.5.6/angular.min.js"></script>
    <script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
    <script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
    <script type="text/javascript">
        var stompClient = null;

        var app = angular.module('app', []);
        app.controller('MainController', function($rootScope, $scope, $http) {

            $scope.data = {
                connected : false,
                sendMessage : '',
                receivMessages : []
            };

            //连接
            $scope.connect = function() {
                var socket = new SockJS('/websocket-endpoint');
                stompClient = Stomp.over(socket);
                stompClient.connect({}, function(frame) {
                    // 订阅后端主动推消息到前端的topic
                    stompClient.subscribe('/topicTest/servertime', function(r) {
                        $scope.data.time = '当前服务器时间:' + r.body;
                        $scope.data.connected = true;
                        $scope.$apply();
                    });
                    // 阅后端主动推消息到前端的topic,只有指定的用户(hzb)收到的的消息
                    stompClient.subscribe('/userTest/hzb/info', function(r) {
                        $scope.data.hzbtime = '当前服务器时间:' + r.body;
                        $scope.data.connected = true;
                        $scope.$apply();
                    });
                    // 订阅前端发到后台,后台又将消息返回前台的topic
                    stompClient.subscribe('/topicTest/web-to-server-to-web', function(msg) {
                        $scope.data.receivMessages.push(msg.body);
                        $scope.data.connected = true;
                        $scope.$apply();
                    });


                    $scope.data.connected = true;
                    $scope.$apply();
                });
            };

            $scope.disconnect = function() {
                if (stompClient != null) {
                    stompClient.disconnect();
                }
                $scope.data.connected = false;
            }

            $scope.send = function() {
                stompClient.send("/app/send", {}, JSON.stringify({
                    'message' : $scope.data.sendMessage
                }));
            }
        });
    </script>
</head>
<body ng-app="app" ng-controller="MainController">
 
  <h2>websocket示例</h2> 
  <label>WebSocket连接状态:</label>
  <button type="button" ng-disabled="data.connected" ng-click="connect()">连接</button>
  <button type="button" ng-click="disconnect()" ng-disabled="!data.connected">断开</button>
  <br/>
  <br/>
  <div ng-show="data.connected">
    <h4>以下是websocket的服务端主动推送消息到页面的例子</h4>
        <label>{{data.time}}</label> <br/> <br/>
    </div>
  <div ng-show="data.connected">
    <h4>以下是websocket的服务端主动推送消息到页面的例子,只有hzb这个用户收到</h4>
        <label>{{data.hzbtime}}</label> <br/> <br/>
    </div>
  <div ng-show="data.connected">
    <h4>以下是websocket的客户端发消息到服务端,服务端再将该消息返回到客户端(页面)的例子</h4>
            <input type="text" ng-model="data.sendMessage" placeholder="请输入内容..." />
        <button ng-click="send()" type="button">发送</button>
        <br/>
        <table>
          <thead>
        <tr>
            <th>消息内容:</th>
          </tr>
          </thead>
          <tbody>
        <tr ng-repeat="messageContent in data.receivMessages">
              <td>{{messageContent}}</td>
          </tr>
          </tbody>
        </table>
    </div>
</body>
</html>

 

运行效果

没有点“连接”之前

 

 点了“连接”之后

 

输入内容:点击发送

 

posted on 2018-09-30 16:44  波神  阅读(4860)  评论(3编辑  收藏  举报