Spring Boot 整合WebSocket(一)

WebSocket简介

WebSocket是一种在单个TCP连接数进行全双工通信的协议,使用WebSocket可以使得用户端和服务器之间的数据交换变得更加简单,它允许服务器主动向客户端推送数据。在WebSocket协议中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行数据双向传输。

WebSocket的特点:

  • WebSocket使用时需要先创建连接,这使得WebSocket成为一种有状态的协议,在之后的通信过程中可以生了一部分状态信息(例如身份认证等)。
  • WebSocket连接在端口80或者443上创建,与HTTP使用的端口相同,这样,基本上所有的防火墙都不会阻止WebSocket连接。
  • WebSocket使用HTTP协议进行握手,因此他可以自然而然地集成到网络浏览器和HTTP服务器中,而不需要额外的成本。
  • 心跳消息将被反复的发送,进而保持WebSocket连接一直处于活跃状态。
  • 使用该协议,当消息启动或者到达的时候,服务器和客户端都可以知道。
  • WebSocket连接关闭时将发送一个特殊的关闭信息。
  • WebSocket支持跨域,可以避免Ajax的限制。
  • HTTP规范要求浏览器将并发连接数限制为每个主机名2个链接,但是当我们使用WebSocket的时候,当握手完成之后,这个限制就不存在了。

WebSocket应用场景:

  • 在线股票网站
  • 即时聊天
  • 多人在线游戏
  • 应用集群通信
  • 系统性能实时监控

Spring Boot 整合WebSocket

消息群发

文件结构

在这里插入图片描述

1、创建项目

首先创建一个SpringBoot 项目,添加如下依赖:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.learn</groupId>
    <artifactId>websocket</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>websocket</name>
    <description>websocket project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- websocket依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>sockjs-client</artifactId>
            <version>1.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>stomp-websocket</artifactId>
            <version>2.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.3.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

spring-boot-starter-websocket依赖是WebSocket相关依赖,其他的都是前端库,使用jar包的形式对这些前端库进行统一管理,使用webjar添加到项目中的前端库,在SpringBoot项目中以及默认添加了静态资源过滤,因此可以直接使用。

2、配置WebSocket

Spring 框架提供了基于WebSocket的STOMP支持,STOMP是一个简单的可交互操作的协议,通常被用于通过中间服务器在客户端之间进行异步消息传递。配置如下:

package com.learn.websocket.configuration;

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

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //设置消息代理的前缀,如果消息的前缀是“/topic”,就会将消息转发给消息代理,再有消息代理将消息广播给当前连接的客户端。
        registry.enableSimpleBroker("/topic");
        //配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //定义一个前缀为“/chat”的endPoint,并开启sockjs支持,sockjs可以解决浏览器对WebSocket的兼容性问题,客户端将通过这里配置的URL来建立WebSocket连接。
        registry.addEndpoint("/chat").withSockJS();
    }
}

3、定义Controller和实体类Message

定义一个Controller用来实现对消息的处理,代码如下:

package com.learn.websocket.controller;

import com.learn.websocket.entity.Message;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class GreetingController {
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) throws Exception{
        return message;
    }

}

根据第二步的配置,@MessageMapping("/hello")注解的方法将用来接收“/app/hello”路径发送来的消息,在注解方法中华队消息进行处理后,再将消息转发到@SendTo定义的路径上,而@SendTo路径是一个前缀为“/topic”的路径,因此该消息将被交给消息队列broker,再由broker进行广播。

创建一个实体类Message,代码如下:

package com.learn.websocket.entity;

public class Message {
    private String name;
    private String content;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

4、构建聊天页面

在resources/static目录下创建chat.html页面作为聊天页面,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
    <div>
        <label for="name">请输入用户名:</label>
        <input type="text" id="name" placeholder="用户名">
    </div>
    <div>
        <button id="connect" type="button">连接</button>
        <button id="disconnect" type="button" disabled="disabled">断开连接</button>
    </div>
    <div  id="chat" style="display: none;">
        <div>
            <label for="content">请输入聊天内容:</label>
            <input type="text" id="content" placeholder="聊天内容">
        </div>
        <button id="send" type="button">发送</button>
        <div id="greetings">
            <div id="conversation" style="display: none;">群聊进行中。。。</div>
        </div>
    </div>
</body>
</html>

第6~8行是引入外库的JS库,这些JS库在pom.xml文件中通过依赖加入进来。app.js是一个自定义JS,代码如下:

var stompClient = null

function setConnected(connected) {
    $("#connect").prop("disabled",connected);
    $("#disconnect").prop("disabled",!connected);
    if(connected){
        $("#conversation").show();
        $("#chat").show();
    }
    else {
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greetings").html("");
}

function connect() {
    if(!$("#name").val()){
        return;
    }
    var socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect({},function (frame) {
        setConnected(true);
        stompClient.subscribe('/topic/greetings',function (greeting) {
            showGreeting(JSON.parse(greeting.body));
        })
    })
}

function disconnect() {
    if (stompClient !== null){
        stompClient.disconnect();
    }
    setConnected(false);
}

function sendName() {
    stompClient.send("/app/hello",{},JSON.stringify({'name': $("#name").val(),'content': $("#content").val()}));
}

function showGreeting(message) {
    $("#greetings").append("<div>"+ message.name +":"+message.content+"</div>")
}

$(function () {
    $("#connect").click(function () {
        connect();
    })

    $("#disconnect").click(function () {
        disconnect();
    })

    $("#send").click(function () {
        sendName();
    })
})

代码解释:

  • connect方法表示建立一个WebSocket连接,在建立WebSocket连接时,用户必须先输入用户名,然后才能建立连接。
  • 第19~26行首先使用SockJS建立连接,然后创建一个STOMP实例发起连接请求,再连接成功的回调方法中,首先调用setConnected(true);方法进行页面的设置,然后调用STOMP中的subscribe方法订阅服务端发送回来的消息,并将服务端发送来的消息展示出来。
  • 调用STOMP中的disconnected方法可以断开一个连接。
5、测试

启动Spring Boot项目进行测试,使用两个浏览器测试。
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值