Spring boot实现WebSocket的单聊与群聊功能

前段时间我写了一篇文章:实现Springboot实时监听mysql数据库的变化且前端实时更新
那篇文章就使用了websocket技术,有兴趣可以看看。
话不多说,开启今天的学习!!!

一、 websocket的简述

WebSocket为浏览器和服务器之间提供了双工异步通信功能,也就是说我们可以利用浏览器给服务器发送消息,服务器也可以给浏览器发送消息,目前主流浏览器的主流版本对WebSocket的支持都算是比较好的,但是在实际开发中使用WebSocket工作量会略大,而且增加了浏览器的兼容问题,这种时候我们更多的是使用WebSocket的一个子协议stomp,利用它来快速实现我们的功能。

二、websocket实战

环境配置
群聊实现
单聊实现


(1)创建spring boot项目,选择 web、websocket还有spring security依赖(此依赖引入的目的下面会讲到)。
在这里插入图片描述
在pom.xml还需添加客户端的依赖:STOMP协议的客户端脚本stomp.js、SockJS的客户端脚本sock.js、jQuery以及WebJars(WebJars能使Maven的依赖管理支持OSS的JavaScript库/CSS库,比如jQuery、Bootstrap等)

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</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-websocket</artifactId>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>sockjs-client</artifactId>
			<version>1.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.4.1</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>stomp-websocket</artifactId>
			<version>2.3.3</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>webjars-locator-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>


(2)先实现群聊的功能,群聊只需将信息转发给每个订阅的用户即可,所以相对于单聊实现较为简单。首先创建socket配置类。

@Configuration
//@EnableWebSocketMessageBroker注解表示开启使用STOMP协议来传输基于代理的消息,Broker就是代理的意思。
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //配置消息的代理
        registry.enableSimpleBroker("/topic");
        //配置接口访问的前缀
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册STOMP协议节点,同时指定使用SockJS协议。
        registry.addEndpoint("/chat").withSockJS();
    }
}

(2)创建群聊消息的实体类

//浏览器发送来的实体类,服务器用message类来接收
public class Message {
    private String name;
    private String content;

(3)编写群聊的web界面,html文件存放于/src/main/resources/static/。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <!--引入pom.xml中引入的web socket的依赖-->
    <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>
</head>
<body>

<table>
    <tr>
        <td>请输入用户名</td>
        <td><input type="text" id="name"></td>
    </tr>
    <tr>
        <td><input type="button" id="connect" value="连接"></td>
        <td><input type="button" id="disconnect" disabled="disabled" value="断开连接"></td>
    </tr>
</table>
<div id="chat" style="display: none">
    <table>
        <tr>
            <td>请输入聊天内容</td>
            <td><input type="text" id="content"></td>
            <td><input type="button" id="send" value="发送"></td>
        </tr>
    </table>
    <div id="conversation">群聊进行中...</div>
</div>
<script>
    $(function () {
        //建立连接
        $("#connect").click(function () {
            connect();
        })
        //断开连接
        $("#disconnect").click(function () {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
        })

        //访问 /app/hello,这边要记得加上根路径,并携带自己的message信息
        $("#send").click(function () {
            stompClient.send('/app/hello',{},JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}))
        })
    })

    var stompClient = null;

    //建立连接
    function connect() {
        if (!$("#name").val()) {
            return;
        }
        // /chat路径是后端注册的STOMP协议节点,
        var socket = new SockJS('/chat');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (success) {
            //连接成功后的回调
            setConnected(true);
            //订阅 /topic/greetings频道,该频道所接收的消息将转发给订阅该频道的所有用户
            stompClient.subscribe('/topic/greetings', function (msg) {
                showGreeting(JSON.parse(msg.body));
            });
        })
    }

    //显示发送的群聊消息
    function showGreeting(msg) {
        $("#conversation").append('<div>' + msg.name + ':' + msg.content + '</div>');
    }

    // 当未建立连接时,连接按钮可以点击,关闭按钮不可以点击,对话框不显示
    // 当连接建立时,连接按钮不可以点击,关闭按钮可以点击,对话框显示
    function setConnected(flag) {
        $("#connect").prop("disabled", flag);
        $("#disconnect").prop("disabled", !flag);
        if (flag) {
            $("#chat").show();
        } else {
            $("#chat").hide();
        }
    }
</script>
</body>
</html>

(4)编写测试的controller。

@Controller
public class TestController {

    //MessageMapping注解和我们之前使用的@RequestMapping类似。
    @MessageMapping("/hello")
    //SendTo表示当服务器有消息需要推送的时候,会对订阅了@SendTo中路径的浏览器发送消息。
    @SendTo("/topic/greetings")
    public Message greeting(Message message) {
        return message;
    }
}

(5)测试群聊。浏览器访问/chat.html,登录之前进行spring security认证用户user密码在控制台复制。
打开两个页面输入两个不同的用户
在这里插入图片描述
在这里插入图片描述
李白发送一条消息,杜甫也可以实时接收到。
在这里插入图片描述
所以群聊功能实现。

(5)实现单聊的功能,在WebSocketConfig配置新的消息代理/queue。

@Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic","/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }

创建单聊用户的实体类

public class Chat {
    private String from;
    private String content;
    private String to;

编写测试的controller

@Controller
public class TestController {

//    MessageMapping注解和我们之前使用的@RequestMapping类似。
//    @MessageMapping("/hello")
//    SendTo表示当服务器有消息需要推送的时候,会对订阅了@SendTo中路径的浏览器发送消息。
//    @SendTo("/topic/greetings")
//    public Message greeting(Message message) {
//        return message;
//    }
    //这是websocket实现好的类,直接注入即可使用,该类是消息发送的模板,利用此模板类可以直接进行消息的转发
    @Autowired
    SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/hello")
    public void greeting(Message message) {
        //实现群聊
        simpMessagingTemplate.convertAndSend("/topic/greetings", message);
    }

    @MessageMapping("/chat")
    public void chat(Principal principal, Chat chat) {
        //实现单聊 
        //principal是spring security返回的用户信息,可以获取当前所登录用户的用户名,
        //注意:推送到特定用户不一定非要使用Spring Security这个框架来实现登录和访问控制,
        //还可以使用的是Apache Shiro
        //chat 前端传来的单聊消息体
        chat.setFrom(principal.getName());
        simpMessagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);
    }
}

(6)创建SecurityConfig配置类,添加两个登录的用户

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("杜甫")
                .password("123")
                .roles("admin")
                .and()
                .withUser("李白")
                .password("123")
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
}

(7)编写单聊的web界面

<!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>
</head>
<body>
<input type="button" id="connect" value="连接">
<input type="button" id="disconnect" disabled="disabled" value="断开连接">
<hr>
消息内容:<input type="text" id="content">目标用户:
<input type="text" id="to">
<input type="button" value="发送" id="send">
<div id="conversation"></div>
<script>
    $(function () {
        $("#connect").click(function () {
            connect();
        })
        $("#disconnect").click(function () {
            if (stompClient != null) {
                stompClient.disconnect();
            }
            setConnected(false);
        })

        $("#send").click(function () {
            stompClient.send('/app/chat', {}, JSON.stringify({
                'to': $("#to").val(),
                'content': $("#content").val()
            }))
        })
    })

    var stompClient = null;

    function connect() {
        var socket = new SockJS('/chat');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (success) {
            setConnected(true);
            //发送给特定的用户时,订阅的路径前加上/user
            stompClient.subscribe('/user/queue/chat', function (msg) {
                showGreeting(JSON.parse(msg.body));
            });
        })
    }

    function showGreeting(msg) {
        $("#conversation").append('<div>' + msg.from + ':' + msg.content + '</div>');
    }

    function setConnected(flag) {
        $("#connect").prop("disabled", flag);
        $("#disconnect").prop("disabled", !flag);
        if (flag) {
            $("#chat").show();
        } else {
            $("#chat").hide();
        }
    }
</script>
</body>
</html>

(8)测试群聊
打开两个不同的浏览器,分别登录杜甫和李白。
登录李白,点击建立连接,此时李白在线。
在这里插入图片描述
登录杜甫,并向李白发送信息,看李白能否实时接收。
在这里插入图片描述
在这里插入图片描述
至此,websocket的单聊也就实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值