java+js+html 实现webSocket广播及私聊

(本项目用的maven为IDEA创建springboot项目时常用的maven依赖)

        <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>
       <!--下面两个依赖为后添加的-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>

1.先创建websocket的配置类  WebSocketConfig

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;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        /*
         * 路径"/webSocket"被注册为STOMP端点,对外暴露(允许跨域),客户端通过该路径接入WebSocket服务
         * setAllowedOrigins("192.168.1.1")  这里添加的是允许访问的站点  我这里写成了*,就是允许所有的客户端接入站点【不安全】
         *
         * html中js创建socket对象时候 用的就是这里的"/webSocket": var socket = new SockJS('/webSocket');
         */
        stompEndpointRegistry.addEndpoint("/webSocket").setAllowedOrigins("*").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        /*
         * 用户可以订阅来自"/topic"和"/user"的消息,
         * 在Controller中,可通过@SendTo注解指明发送目标,这样服务器就可以将消息发送到  订阅相关消息的客户端    【所有人或者某个人】
         *
         * 在本例子中,使用topic来达到群发效果,使用user进行一对一发送
         *
         * 客户端只可以订阅这两个前缀的主题
         */
        registry.enableSimpleBroker("/topic", "/user");

        /*
         * 客户端发送过来的消息,需要以"/topic"为前缀,再经过Broker转发给响应的Controller
         * 订阅主题:'/topic/get/broadcast'
         * 推送方式:1.@SendTo("/topic/get/broadcast")
         *           2.ResponseMessage responseMessage = new ResponseMessage("hello everyone!");
         */
        registry.setApplicationDestinationPrefixes("/topic");

        /*
         * 一对一发送的前缀
         * 订阅主题(html):'/user/huoLaoShu/get/somebodyMessage'
         * 推送方式:1、@SendToUser("/get/somebodyMessage")
         *          2、messagingTemplate.convertAndSendToUser(userName, "/get/somebodyMessage", responseMessage);;
         */
        registry.setUserDestinationPrefix("/user");
    }
}

2.然后创建springboot项目  控制层  WsController  【类中引用了两个我自定义的bean:RequestMessage 和 ResponseMessage】

package org.xlx.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.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.xlx.websocket.bean.RequestMessage;
import org.xlx.websocket.bean.ResponseMessage;

@Controller
@CrossOrigin
public class WsController {
    /*
     * 使用restful风格
     */
    private final SimpMessagingTemplate messagingTemplate;

    /*
     * 实例化Controller的时候,注入SimpMessagingTemplate
     */
    @Autowired
    public WsController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    /**
     * 广播
     * @param message
     * @return
     */
    @MessageMapping("/send/broadcast")   //前台发送广播消息的时候, 地址为 /topic/send/broadcast
    @SendTo("/topic/get/broadcast")   //前台订阅这个消息的时候 为  /topic/get/broadcast
   // 因为我html里传过来的值为 {"name": "发送内容"}   所以接收的对象可以直接转成我自己定义的bean RequestMessage
    // 【里面只有一个name属性】 后续可以根据自己的业务逻辑修改自己需要的类
    public ResponseMessage broadcast(@RequestBody RequestMessage message, StompHeaderAccessor headerAccessor){

       //TODO  此处可以添加自己的业务逻辑

//        将 message.getName()  通过广播发送给所有订阅了广播的 在线客户端
        ResponseMessage responseMessage = new ResponseMessage(message.getName());

        return responseMessage;
    }

    /**
     * 发送私聊
     * @param message
     * @param headerAccessor
     * @return
     */
    @MessageMapping("/send/somebody")  //前台发送私聊消息的时候, 地址为 /topic/send/somebody  例如:stompClient.send("/topic/send/somebody", {"username": "xuelongxin"}, JSON.stringify({"data": message}));
    @SendToUser("/get/somebodyMessage")   //前台订阅这个私聊消息的时候 为  /user/{userName}/get/somebodyMessage   例如: stompClient.subscribe('/user/xuelongxin/get/somebodyMessage', function (response) { 处理response })
    public ResponseMessage sendSomebody(@RequestBody RequestMessage message,StompHeaderAccessor headerAccessor) {
        //获取{"username": "huolaoshu"} 中的huolaoshu 确定私聊是给谁发的
        // 这里 headerAccessor.getNativeHeader("username")   的值是 :[huolaoshu]
        // 至于为啥会有个中括号  我也不清楚
        String userName = headerAccessor.getNativeHeader("username").toString().replace("[","").replace("]","");
        //把消息放到responseMessage 后定向发送 给  huolaoshu
        ResponseMessage responseMessage = new ResponseMessage(message.getName());
        messagingTemplate.convertAndSendToUser(userName, "/get/somebodyMessage", responseMessage);
        return responseMessage;
    }
}

3.两个自定义类:

public class RequestMessage {

    private String name;

    public String getName() {
        return name;
    }
}
public class ResponseMessage {
    private String responseMessage;

    public ResponseMessage(String responseMessage) {
        this.responseMessage = responseMessage;
    }

    public String getResponseMessage() {
        return responseMessage;
    }
}

4.接下来是html+js文件

(1)guangbo.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>广播式WebSocket</title>
<!--    附件 stomp支持js-->
    <script src="js/sockjs.min.js"></script>
    <script src="js/stomp.min.js"></script>
</head>
<!--页面加载后 如果原来就有socket对象  就先断开-->
<body onload="disconnect()">
<!--检测浏览器是否支持websocket -->
<noscript><h2 style="color: #e80b0a;">Sorry,浏览器不支持WebSocket</h2></noscript>
<div>
    <div>
        <!-- 创建并注册 websocket  这里注册的是广播【本页面连带发送和接收广播】-->
        <button id="connect" onclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
    </div>

    <div id="conversationDiv">
        <label>输入你的名字</label>
        <input type="text" id="name"/>
        <!-- 点击发送后  就能发送广播 -->
        <button id="sendName" onclick="sendName();">发送</button>
        <p id="response"></p>
    </div>
</div>
<script type="text/javascript">
    var stompClient = null;

    //设置当前的连接状态
    function setConnected(connected) {
        document.getElementById("connect").disabled = connected;
        document.getElementById("disconnect").disabled = !connected;
        document.getElementById("conversationDiv").style.visibility = connected ? 'visible' : 'hidden';
        document.getElementById("response").innerHTML = "";
    }


    //点击连接 时候触发的事件 【这个函数 除了更改自己url 或者添加自己的业务逻辑  一般不用修改 】
    function connect() {
        //创建socket对象  和服务器的 /webSocket 注册连接
        var socket = new SockJS('/webSocket');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function () {
            setConnected(true);
            //注册到springboot中controller中的广播中  --》 @SendTo("/topic/get/broadcast")
            //和controller里的 @SendTo("/topic/get/broadcast") 相对应
            stompClient.subscribe('/topic/get/broadcast', function (response) {
                //这里获取 从后台 传来的数据  然后使用下面的 showResponse() 函数展示到页面
                showResponse(JSON.parse(response.body).responseMessage);
            })
        });
    }

    //断开连接
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        }
        setConnected(false);
        console.log('Disconnected');
    }

    //发送私聊
    function sendName() {
        var name = document.getElementById("name").value;
        stompClient.send("/topic/send/broadcast", {}, JSON.stringify({"name": name}));
    }

    //展示 返回的消息
    function showResponse(message) {
        document.getElementById("response").innerHTML = message;
    }
</script>
</body>
</html>

(2)siliao.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8"/>
  <title>广播式WebSocket</title>
  <!--    附件 stomp支持js-->
  <script src="js/sockjs.min.js"></script>
  <script src="js/stomp.min.js"></script>
</head>
<!--页面加载后 如果原来就有socket对象  就先断开-->
<body onload="disconnect()">
<!--检测浏览器是否支持websocket -->
<noscript><h2 style="color: #e80b0a;">Sorry,浏览器不支持WebSocket</h2></noscript>
<div>
  <div>
    <!-- 创建并注册 websocket  这里注册的是广播【本页面连带发送和接收广播】-->
    <button id="connect" onclick="connect();">连接</button>
    <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
  </div>

  <div id="conversationDiv">
    <label>输入你的名字</label>
    <input type="text" id="name"/>
    <!-- 点击发送后  就能发送私信-->
    <button id="sendName" onclick="sendName();">发送</button>
    <p id="response"></p>
  </div>
</div>
<script type="text/javascript">
  var stompClient = null;

  //设置当前的连接状态
  function setConnected(connected) {
    document.getElementById("connect").disabled = connected;
    document.getElementById("disconnect").disabled = !connected;
    document.getElementById("conversationDiv").style.visibility = connected ? 'visible' : 'hidden';
    document.getElementById("response").innerHTML = "";
  }


  //点击连接 时候触发的事件 【这个函数 除了更改自己url 或者添加自己的业务逻辑  一般不用修改 】
  function connect() {
    //创建socket对象  和服务器的 /webSocket 注册连接
    var socket = new SockJS('/webSocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function () {
      setConnected(true);
      //  这里是向服务器注册    这里的huolaoshu  就是给服务器说   凡是要发给huolaoshu的信息 都给我发过来   你可以修改成登录者的id
      //和 controller里的 @SendToUser("/get/somebodyMessage")  相对应   前面的/user 和 WebSocketConfig类里的配置对应
      stompClient.subscribe('/user/huolaoshu/get/somebodyMessage', function (response) {
        //这里获取 从后台 传来的数据  然后使用下面的 showResponse() 函数展示到页面
        showResponse(JSON.parse(response.body).responseMessage);
      })
    });
  }

  //断开连接
  function disconnect() {
    if (stompClient != null) {
      stompClient.disconnect();
    }
    setConnected(false);
    console.log('Disconnected');
  }

  //发送私聊
  function sendName() {
    var name = document.getElementById("name").value;
    // 我这里只发送给了 huolaoshu  这个人[也就是我自己,这里为了好测试],
    // 以后可以根据自己项目业务,改成某人的id,然后进行私发
    //后面的  JSON.stringify({"name": name}) 是要发送给服务器的私聊信息
    stompClient.send("/topic/send/somebody", {"username": "huolaoshu"}, JSON.stringify({"name": name}));
  }

  //展示 返回的消息
  function showResponse(message) {
    document.getElementById("response").innerHTML = message;
  }
</script>
</body>
</html>

5.两个html文件均引用了两个js文件 【sockjs.min.js和stomp.min.js】因为这两个js文件为第三方提供的库,太长,这里我提供下载地址:sockjs.min.js+stomp.min.js-Javascript文档类资源-CSDN下载sockjs.min.js+stomp.min.jshtml+js实现websocket的第更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/q18792880831/85547431

6.下面附上项目 目录

 7.实现的简单效果:

【广播】

 

 【私聊】

8.各种解释已经写在代码注释里了,这里主要是实现了 用springboot作为后台,html+js为前台,实现简单的广播 和 私聊 功能,私聊这里我只是发送给了我自己   大家可以试试 多注册几个不同的用户【最简单的就是再新建几个siliao.html  然后修改里面的

stompClient.subscribe('/user/huolaoshu/get/somebodyMessage', function (response) {xxxxxx}

huolaoshu 然后只私发给huolaoshu  看看其他人是否能收到消息】大家可以多做尝试 ,如果对以上代码有任何疑问的,欢迎在评论区留言。

猜你喜欢

转载自blog.csdn.net/q18792880831/article/details/125125276