springbootSpringBoot杂谈随笔-生活工作点滴

Spring Boot——整合WebSocket(基于STOMP

2019-07-09  本文已影响80人  Hi_JIAQI

WebSocket 与HTTP 的简单区别:

WebSocket 简单使用:

1.添加pom文件依赖:
        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
2.直接上源码:
@Repository
public class MyHandler extends TextWebSocketHandler {
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println("获取到消息》》》》" + message.getPayload());
        session.sendMessage(new TextMessage("消息已收到"));
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        session.sendMessage(new TextMessage("欢迎连接到ws服务"));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("断开连接");
    }
}

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    MyHandler myHandler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
        webSocketHandlerRegistry.addHandler(this.myHandler,"/ws").setAllowedOrigins("*");
    }
}
3.我们可以通过在线测试工具进行测试:

基于STOMP协议的WebSocket的使用:

它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互,类似于OpenWire(一种二进制协议)

STOMP协议工作于TCP协议之上,使用了下列命令:
1.一样先WebSocket的添加pom文件依赖
2.配置websocket stomp(代码中均有做解释)
/**
 * 通过EnableWebSocketMessageBroker
 * 开启使用STOMP协议来传输基于代理(message broker)的消息,
 * 此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 注册stomp的端点
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) { 
        //endPoint 注册协议节点,并映射指定的URl点对点-用
        //注册一个名字为"endpointChat" 的endpoint,并指定 SockJS协议。
        //允许使用socketJs方式访问,访问点为webSocketServer,允许跨域
        registry.addEndpoint("/endpointChat")
                .setAllowedOrigins("*")
                .withSockJS();
    }

    /**
     * 配置消息代理(message broker)
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(message broker)
        //订阅Broker名称:topic 代表发布广播,即群发
        //queue 代表点对点,即发指定用户
        registry.enableSimpleBroker("/queue", "/topic");
        // 全局使用的消息前缀(客户端订阅路径上会体现出来)
        registry.setApplicationDestinationPrefixes("/app");
        //点对点使用的订阅前缀(客户端订阅路径上会体现出来),
        // 不设置的话,默认也是/user/
        // registry.setUserDestinationPrefix("/user/");
    }
}
3.消息实体类

客户端发往服务器端实体类(可自定义)

public class RequestMessage {
    private String name;

    public String getName() {
        return name;
    }
}

服务器端发往客户端实体类(可自定义)

public class ResponseMessage {
    private String responseMessage;

    public String getResponseMessage() {
        return responseMessage;
    }

    public void setResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }

    public ResponseMessage() {

    }
    public ResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }
}
4.控制层
@Controller
public class WebSockedController {

    /**
     * @param requestMessage
     * @return
     * @MessageMapping 指定要接收消息的地址,类似@RequestMapping。除了注解到方法上,也可以注解到类上
     * @SendTo默认 消息将被发送到与传入消息相同的目的地
     */
    @MessageMapping("/sendTest")
    @SendTo("/topic/subscribeTest")
    public ResponseMessage broadcast(RequestMessage requestMessage) {
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setResponseMessage("你发送的消息为:" + requestMessage.getName());
        return responseMessage;
    }

    @SubscribeMapping("/subscribeTest")
    public ResponseMessage sub() {
        ResponseMessage responseMessage = new ResponseMessage();
        responseMessage.setResponseMessage("感谢你订阅了我");
        return responseMessage;
    }

    @Autowired
    SimpMessagingTemplate template;

    @GetMapping("/indexaaa")
    @ResponseBody
    public Result gg() {
        template.convertAndSend("/topic/getResponse", new ResponseMessage("通过SimpMessagingTemplate进行消息广播该方式无法直接被使用,需要建立连接后访问使用,"));

        return Result.success();
    }

    @RequestMapping(value = "/index")
    public String broadcastIndex(HttpServletRequest req) {
        System.out.println(req.getRemoteHost());
        return "index";
    }

}
5.前端代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试</title>
</head>
<body>
<h1>websocket!!!!</h1>
<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<button onclick="subscribe1()">订阅消息/topic/subscribeTest</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body>
<script src="https://cdn.bootcss.com/jquery/2.1.4/jquery.min.js"></script>
<script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script>
    // websocket的连接地址:表示连接的SockJS的endpoint名称为/endpointChat
    var socket = new SockJS("/endpointChat");
    // 使用STOMP来创建WebSocket客户端
    var stompClient = Stomp.over(socket);
    //调用stompClient中的connect方法来连接服务端
    // 向服务器发起websocket连接并发送CONNECT帧
    stompClient.connect({}, function connectCallback(frame) {
            // 连接成功时(服务器响应 CONNECTED 帧)的回调方法
            setMessageInnerHTML("连接成功");
            //调用stompClient中的subscribe方法来订阅/topic/getResponse发送来的消息
            //也就是我们在Controller中的say方法上添加的@SendTo注解的参数。
            //stompClient中的send方法表示发送一条消息到服务端,
            // 客户端订阅消息的目的地址:此值BroadcastCtl中被@SendTo("/topic/subscribeTest")注解的里配置的值

            stompClient.subscribe('/topic/getResponse', function (response) {
                console.log(response);
                var returnData = JSON.parse(response.body);
                setMessageInnerHTML("/topic/getResponse 你接收到的消息为:" + returnData.responseMessage);
            });
        },
        function errorCallBack(error) {
            // 连接失败时(服务器响应 ERROR 帧)的回调方法
            setMessageInnerHTML("连接失败");
        }
    );


    //发送消息
    function send() {
        // 客户端消息发送的目的:服务端使用BroadcastCtl中
        // @MessageMapping("/sendTest")注解的方法来处理发送过来的消息
        var message = document.getElementById('text').value;
        var messageJson = JSON.stringify({"name": message});
        stompClient.send("/app/sendTest", {}, messageJson);
        setMessageInnerHTML("/app/sendTest 你发送的消息:" + message);
    }

    //订阅消息
    function subscribe1() {
        stompClient.subscribe('/app/subscribeTest', function (response) {
            setMessageInnerHTML("已成功订阅/topic/subscribeTest1");
            var returnData = JSON.parse(response.body);
            setMessageInnerHTML("/topic/subscribeTest 你接收到的消息为:" + returnData.responseMessage);
        });
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //断开
    function closeWebSocket() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setMessageInnerHTML("关闭连接成功");

    }
</script>
</html>
6.功能详解

@MessageMapping(“/sendTest”)
接收客户端发送的消息,当客户端发送消息的目的地为/app/sendTest时,交给该注解所在的方法处理消息,其中/app是在WebSocketConfig配置文件configureMessageBroker方法中添加:
registry.setApplicationDestinationPrefixes("/app");
若没有添加@SendTo注解且该方法有返回值,则返回的目的地地址为/topic/sendTest,经过消息代理,客户端需要订阅了这个主题才能收到返回消息

@SubscribeMapping(“/subscribeTest”)
接收客户端发送的订阅,当客户端订阅的目的地为/app/subscribeTest时,交给该注解所在的方法处理订阅,其中/app为客户端请求前缀
若没有添加@SendTo注解且该方法有返回值,则返回的目的地地址为/app/sendTest,不经过消息代理,客户端需要订阅了这个主题才能收到返回消息

@SendTo(“/topic/subscribeTest”)
修改返回消息的目的地地址为/topic/subscribeTest,经过消息代理,客户端需要订阅了这个主题才能收到返回消息

通过@SubscribeMapping 、SimpMessagingTemplate (服务器主动推送)的形式所订阅的地址结果(如果点击关闭WebSocket连接后将无法获取订阅消息)
通过@SendTo所订阅的地址结果
整理的不是很全不过点对点传输也是如此,在send连接中多一个/user即可实现,只不过在后端需要注意差别,该笔记有参考过网上一些博客前辈的写法,若有不足之处还欢迎指点!!!
上一篇下一篇

猜你喜欢

热点阅读