Springboot 集成 webscoket 实现系统给用户发信息, 用户界面右上角有个小铃铛

本文详细介绍了如何在Springboot应用中集成WebSocket,实现系统向用户发送信息的功能。涉及到WebSocket的配置类、信号量控制并发连接数、消息处理类以及Nginx的配置以支持WebSocket。此外,还展示了前端页面的HTML和Vue实现,用于连接和接收服务器推送的消息。
摘要由CSDN通过智能技术生成

Springboot 集成webscoket 实现系统给用户发信息, 用户界面右上角有个小铃铛

一. 引入pom.xml ,启动类记得添加注解:@EnableWebSocket
<!-- websocket  依赖  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二. 创建配置类:WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * websocket 配置
 *
 * @author yzz
 */
@Configuration
public class WebSocketConfig
{
    @Bean
    public ServerEndpointExporter serverEndpointExporter()
    {
        return new ServerEndpointExporter();
    }
}
三. 信号量相关处理类:SemaphoreUtils
import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 信号量相关处理
 *
 * @author xx
 */
public class SemaphoreUtils
{
    /**
     * SemaphoreUtils 日志控制器
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);

    /**
     * 获取信号量
     *
     * @param semaphore
     * @return
     */
    public static boolean tryAcquire(Semaphore semaphore)
    {
        boolean flag = false;

        try
        {
            flag = semaphore.tryAcquire();
        }
        catch (Exception e)
        {
            LOGGER.error("获取信号量异常", e);
        }

        return flag;
    }

    /**
     * 释放信号量
     *
     * @param semaphore
     */
    public static void release(Semaphore semaphore)
    {

        try
        {
            semaphore.release();
        }
        catch (Exception e)
        {
            LOGGER.error("释放信号量异常", e);
        }
    }
}
四. 消息处理类:MyWebSocket
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Semaphore;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * MyWebSocket 消息处理
 *
 * @author xx
 */
@Component
@ServerEndpoint("/websocket/message/{userId}")
public class MyWebSocket
{
    /*** WebSocketServer 日志控制器*/
    private static final Logger LOGGER = LoggerFactory.getLogger(MyWebSocket.class);
    /*** 默认最多允许同时在线人数100*/
    public static int socketMaxOnlineCount = 100;
    private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);
    /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /** concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     在外部可以获取此连接的所有websocket对象,并能对其触发消息发送功能,我们的定时发送核心功能的实现在与此变量 */
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    // 接收userId
    private String userId = "";
    public static Map<String,String> body;
    static
    {
        body = new HashMap<String, String>();
        body.put("-1", "-1");
    }

    /**
     * 连接建立成功调用的方法
     * 注意这里的userid,前端请求webscoket要这样:
     * ws://127.0.0.1:8080/websocket/message/2
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) throws Exception
    {
        this.session = session;
        boolean semaphoreFlag = false;
        // 尝试获取信号量
        semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!semaphoreFlag)
        {
            // 未获取到信号量
            LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);
            sendMessage("当前在线人数超过限制数:" + socketMaxOnlineCount);
            session.close();
        }else{
            this.userId = userId;
            for (MyWebSocket webSocket:webSocketSet){
                if (webSocket.getUserId().equals(userId)){
                    webSocketSet.remove(webSocket);
                    subOnlineCount();
                    body.remove(userId);
                    System.out.println("用户"+userId+"连接关闭当前人数为"+getOnlineCount());
                }
            }
            try {
                body.put(userId,null);
                webSocketSet.add(this);     //加入set中
                addOnlineCount();           //在线数加1
                System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());

                //  sendMessage("连接已建立成功.");
            } catch (Exception e) {
                System.out.println("IO异常");
            }

        }
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session)
    {
        LOGGER.info("\n 关闭连接 - {}", session);
        MyWebSocket webSocket = this;
        boolean b=false;
        for (MyWebSocket socket:webSocketSet){
            if (socket.equals(webSocket)){
                b=true;
            }
        }
        if (b) {
            //连接关闭后,将此websocket从set中删除
            webSocketSet.remove(this);
            subOnlineCount();           //在线数减1
            body.remove(userId);
            // 获取到信号量则需释放
            SemaphoreUtils.release(socketSemaphore);
        }
        LOGGER.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void onError(Session session, Throwable exception) throws Exception
    {
        if (session.isOpen())
        {
            // 关闭连接
            session.close();
        }
        String sessionId = session.getId();
        LOGGER.info("\n 连接异常 - {}", sessionId);
        LOGGER.info("\n 异常信息 - {}", exception);

        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session)
    {
        String msg = message.replace(" ", "");
        LOGGER.info("\n 来自客户端的消息: {}", msg);
    }

    /**
     * 发送消息,在定时任务中会调用此方法*
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);

    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }
    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }

    public Session getSession() {
        return session;
    }
    public void setSession(Session session) {
        this.session = session;
    }
    public static CopyOnWriteArraySet<MyWebSocket> getWebSocketSet() {
        return webSocketSet;
    }
    public static void setWebSocketSet(CopyOnWriteArraySet<MyWebSocket> webSocketSet) {
        MyWebSocket.webSocketSet = webSocketSet;
    }
    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}
五. 系统给用户发送信息。

注:使用了定时任务。后管点击发送信息信息保存到数据库通知管理表,然后通过定时任务去获取然后发送给指定的客户,代码如下:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;

  @Scheduled(cron = "0/1 * * * * ?")   //每秒执行一次
    public void test(){
        //1.获取所有在线连接
        CopyOnWriteArraySet<MyWebSocket> webSocketSet =MyWebSocket.getWebSocketSet();
        webSocketSet.forEach(c->{
            try {
                //客户id
                String userId = c.getUserId();
                //查询通知管理表
                LambdaQueryWrapper<BusiNotice> wrapper=new LambdaQueryWrapper();
                wrapper.eq(BusiNotice::getToUser, Integer.parseInt(userId));//接收人
                wrapper.eq(BusiNotice::getLanguageType,"cn");//语言
                wrapper.eq(BusiNotice::getStatus,0);//0未读,1已读
                wrapper.in(BusiNotice::getType,0,2);//0系统通知,1首页通知,2邮件站内信
                wrapper.orderByDesc(BusiNotice::getCreateTime);
                List<BusiNotice> list = noticeService.list(wrapper);
                //将信息处理成json字符串
                Gson gson = new Gson();
                String listToJsonString = gson.toJson(list);
                //查询缓存里面是否有之前发送过的信息,
                //是null ,直接发送,并放入缓存中
                //不是null,判断处理的json和缓存里的是否一样,不同:发送并放入缓存中|| 相同:不发送
                String s = MyWebSocket.body.get(userId);
                if (!ObjectUtils.isEmpty(s)) {
                    if (listToJsonString.equals(s)) {
                        return;
                    } else {
                        c.sendMessage(listToJsonString);
                        MyWebSocket.body.put(userId,listToJsonString);
                        logger.info("用户"+userId+"发送成功");
                    }
                }else {
                    c.sendMessage(listToJsonString);
                    MyWebSocket.body.put(userId,listToJsonString);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
     }
五. 2)nginx 配置
     server {
        listen  443;
        server_name XXXXX.com.cn;
        ssl_certificate /web/ssl/cn1.pem;
        ssl_certificate_key /web/ssl/cn1.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
        ssl_prefer_server_ciphers on;
        location / {
            root /web/cn/index;
            try_files $uri $uri/ /index.html =404;
        }
        location /api/ {
            proxy_pass http://127.0.0.1:9999/;
            proxy_buffering off;
            proxy_connect_timeout 15s;
            proxy_send_timeout 15s;
            proxy_read_timeout 15s;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       }
        location /wss/ {
            proxy_pass http://127.0.0.1:9999/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-real-ip $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
    	}
}
六. 1)前端页面: webscoket.html

**注意:如果是要通过 nginx 那么链接要改成:wss://XXXXX.com.cn/wss/websocket/message/1
XXXXX.com.cn表示域名

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>测试界面</title>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" ></script>
</head>

<body>

<div>
    <input type="text" style="width: 20%" value="ws://127.0.0.1:8080/websocket/message/2" id="url">
	<button id="btn_join">连接</button>
	<button id="btn_exit">断开</button>
</div>
<br/>
<textarea id="message" cols="100" rows="9"></textarea> <button id="btn_send">发送消息</button>
<br/>
<br/>
<textarea id="text_content" readonly="readonly" cols="100" rows="9"></textarea>返回内容
<br/>
<br/>

<script type="text/javascript">
    $(document).ready(function(){
        var ws = null;
        // 连接
        $('#btn_join').click(function() {
        	var url = $("#url").val();
            ws = new WebSocket(url);
            ws.onopen = function(event) {
                $('#text_content').append('已经打开连接!' + '\n');
            }
            ws.onmessage = function(event) {
                $('#text_content').append(event.data + '\n');
            }
            ws.onclose = function(event) {
                $('#text_content').append('已经关闭连接!' + '\n');
            }
        });
        // 发送消息
        $('#btn_send').click(function() {
            var message = $('#message').val();
            if (ws) {
                ws.send(message);
            } else {
                alert("未连接到服务器");
            }
        });
        //断开
        $('#btn_exit').click(function() {
            if (ws) {
                ws.close();
                ws = null;
            }
        });
    })
</script>
</body>
</html>
六. 2)前端页面: webscoket.vue
<template>
  <div>
    <el-input v-model="url" type="text" style="width: 20%" /> &nbsp; &nbsp;
    <el-button @click="join" type="primary">连接</el-button>
    <el-button @click="exit" type="danger">断开</el-button>

    <br />
    <el-input type="textarea" v-model="message" :rows="9" />
    <el-button type="info" @click="send">发送消息</el-button>
    <br />
    <br />
    <el-input type="textarea" v-model="text_content" :rows="9" /> 返回内容
    <br />
    <br />
  </div>
</template>

<script>
export default {
  data() {
    return {
      url: "ws://127.0.0.1:8080/websocket/message/2",
      message: "",
      text_content: "",
      ws: null,
    };
  },
  methods: {
    join() {
      const wsuri = this.url;
      this.ws = new WebSocket(wsuri);
      const self = this;
      this.ws.onopen = function (event) {
        self.text_content = self.text_content + "已经打开连接!" + "\n";
      };
      this.ws.onmessage = function (event) {
        self.text_content = event.data + "\n";
      };
      this.ws.onclose = function (event) {
        self.text_content = self.text_content + "已经关闭连接!" + "\n";
      };
    },
    exit() {
      if (this.ws) {
        this.ws.close();
        this.ws = null;
      }
    },
    send() {
      if (this.ws) {
        this.ws.send(this.message);
      } else {
        alert("未连接到服务器");
      }
    },
  },
};
</script>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现Spring Boot集成WebSocket实现消息推送,需要进行以下步骤: 1. 添加Spring Boot WebSocket依赖 2. 创建WebSocket配置类 3. 创建WebSocket处理器类 4. 创建WebSocket拦截器类 5. 创建WebSocket消息模型类 6. 在Spring Boot使用WebSocket 下面是一个简单的示例代码: 1. 添加Spring Boot WebSocket依赖 在pom.xml文件添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建WebSocket配置类 创建一个WebSocket配置类,用于配置WebSocket相关的参数,如下所示: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/websocket").addInterceptors(new WebSocketInterceptor()); } } ``` 3. 创建WebSocket处理器类 创建一个WebSocket处理器类,用于处理WebSocket连接、断开连接和接收消息等操作,如下所示: ``` public class WebSocketHandler extends TextWebSocketHandler { private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(message); } } } ``` 4. 创建WebSocket拦截器类 创建一个WebSocket拦截器类,用于拦截WebSocket连接请求,如下所示: ``` public class WebSocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } } ``` 5. 创建WebSocket消息模型类 创建一个WebSocket消息模型类,用于封装WebSocket消息,如下所示: ``` public class WebSocketMessage { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } } ``` 6. 在Spring Boot使用WebSocket 在Spring Boot使用WebSocket非常简单,只需要在Controller注入WebSocketSession即可,如下所示: ``` @Controller public class WebSocketController { @Autowired private WebSocketSession session; @MessageMapping("/send") public void send(WebSocketMessage message) throws Exception { session.sendMessage(new TextMessage(message.getContent())); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值