springboot+webSocket实现服务端向客户端推送消息

springboot+webSocket实现服务端向客户端推送消息(扫码登录)

Java实现Websocket通常有两种方式:
1、创建WebSocketServer类,里面包含open、close、message、error等方法;
2、利用Springboot提供的webSocketHandler类,创建其子类并重写方法。我们项目虽然使用Springboot框架,不过仍采用了第一种方法实现

(1)websocket依赖

<!--websocket支持包-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

(2)创建配置类WebSocketConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    /**
     * 如果使用Springboot默认内置的tomcat容器,则必须注入ServerEndpoint的bean;
     * 如果使用外置的web容器,则不需要提供ServerEndpointExporter,下面的注入可以注解掉
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

(3)创建webocketServer

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 * @ServerEndpoint 可以把当前类变成websocket服务类
 */
@Slf4j
@ServerEndpoint("/websocket/{userName}/{deptId}")
@Component
public class WebSocketServer {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userName")String userName,@PathParam("deptId")Integer deptId) throws IOException {
        this.session = session;
        //加入set中
        webSocketSet.add(this);
        //在线数加1
        addOnlineCount();
       ~~业务~~ 
        log.info("有新连接加入!当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("userName")String userName,@PathParam("deptId")Integer deptId){
        //从set中删除
        webSocketSet.remove(this);
        //在线数减1
        subOnlineCount();
       ~~业务~~ 
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session,@PathParam("userName")String userName,@PathParam("deptId")Integer deptId) {
        log.info("客户端发送的消息{},客户端连接个数,{}",message,onlineCount);
        ~~业务~~ 
    }

    /**
     * 发生错误时调用
     */
     @OnError
     public void onError(Session session, Throwable error) throws IOException {
         log.error("发生错误:{},SessionID: {}",error.getMessage(),session.getId());
         this.session.getBasicRemote().sendText("error");
         error.printStackTrace();
     }

    /**
     * 实现服务器主动推送
     */
     public void sendMessage(String message) throws IOException {
         try {
             log.info("后台向前台发送数据:{}",message);
             this.session.getBasicRemote().sendText(message);
         } catch (IOException e) {
             log.error("发送消息出错:{}", e.getMessage());
             e.printStackTrace();
         }
     }


    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}


(4)服务端提供接口进行消息推送

import com.alibaba.fastjson.JSON;
import com.breezedi.common.core.controller.BaseController;
import com.breezedi.common.core.domain.AjaxResult;
import com.breezedi.common.utils.DateUtils;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 二维码相关
 */
@CrossOrigin
@RestController
@RequestMapping("/qrcode")
@Slf4j
public class QRCodeController extends BaseController {

    //需要验证的用户保存到这里
    public static Map<String,String> waitForUserValid  = new HashMap<>();

    //定时缓存用户数据==判斷60秒
    public static Cache<String, Object> numCache = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();

    //定时缓存用户数据
    public static Cache<String, Object> numCacheScan = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();

    //验证
    public static Cache<String, Object> validation = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();

    //用户扫码购买取消长连接
    public static Cache<String, Object> buyPkgValidation = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();

    /**
     * 门槛验证时通过手机扫码
     */
    @GetMapping(value = "/api/scan/{userName}/{deptId}/{cpId}/{userId}")
    public AjaxResult scanChooseCpUser(@PathVariable("userName")String userName, @PathVariable("deptId")Long deptId,@PathVariable("cpId")Long cpId,@PathVariable("userId")Long userId,@RequestParam(required = false) Long pkgId,@RequestParam(required = false) Long detailId){
         ~~业务~~ 
        ~~if (waitForUserValid.containsKey(userName+"-"+deptId)){
            waitForUserValid.put(userName+"-"+deptId,sb.toString());
        }
        numCacheScan.invalidate(userName+"-"+deptId);~~ 
        return AjaxResult.success();
    }

}

(5)客户端调用消息推送接口接受消息

客户端调用发送消息的链接: wss://域名+路径/websocket/参数/参数

<!DOCTYPE HTML>getOrderList
<html>
<head>
    <meta charset="utf-8">
    <title>菜鸟教程(runoob.com)</title>
    <script type="text/javascript">
        function clock(){
            console.log(1);
        }
        function WebSocketTest(){
            if ("WebSocket" in window){
                alert("您的浏览器支持 WebSocket!");
                // 打开一个 web socket
                var ws = new WebSocket(`ws://localhost:9083/admin/websocket/aaaa/103`);
                ws.onopen = function(){
                    // Web Socket 已连接上,使用 send() 方法发送数据
                    ws.send("发送数据");
                    alert("数据发送中...");
                    self.setInterval(function (){
                        ws.send("help");
                    },1000);
                };
                ws.onmessage = function (evt){
                    var received_msg = evt.data;
                    console.log("前台接收到后台推送的数据:"+received_msg);
                    ws.close();
                    alert("数据已接收...");
                };
                ws.onclose = function(){
                    // 关闭 websocket
                    alert("连接已关闭...");
                };
            }
            else{
                // 浏览器不支持 WebSocket
                alert("您的浏览器不支持 WebSocket!");
            }
        }
    </script>
</head>
<body>
<div id="sse">
    <a href="javascript:WebSocketTest()">运行 WebSocket</a>
</div>
</body>
</html>

(6)nginx配置wss访问

	server {
			listen       443 ssl;
	        server_name  ~~xxxxxxxxxxxx~~ ;
			ssl_certificate     ~~xxxxxxxxxxx~~ ;
	        ssl_certificate_key  ~~xxxxxxxxxxxxxxx~~ ;
			ssl_session_timeout 5m;
	      
			#websocket配置
			location /admin/websocket {  #注意路径要写对
				proxy_pass http://127.0.0.1:9083/admin/websocket;
				
				proxy_http_version 1.1;
				proxy_connect_timeout 4s;
				proxy_read_timeout 7200s;
				proxy_send_timeout 12s;
				proxy_set_header Upgrade $http_upgrade;
				proxy_set_header Connection "upgrade";
			}
		  }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 Spring BootWebSocket 和 Vue 实现后端向前端实时推送数据的代码示例: 1. 后端代码 ``` @Controller public class WebSocketController { private final WebSocketService webSocketService; @Autowired public WebSocketController(WebSocketService webSocketService) { this.webSocketService = webSocketService; } @GetMapping("/") public String index() { return "index"; } @MessageMapping("/send") public void send(String message) { webSocketService.sendAll(message); } @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ``` ``` @Service public class WebSocketService { private final List<Session> sessions = new CopyOnWriteArrayList<>(); public void add(Session session) { sessions.add(session); } public void remove(Session session) { sessions.remove(session); } public void sendAll(String message) { sessions.forEach(session -> { try { session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } }); } } ``` 2. 前端代码 ``` <template> <div> <h1>Real-time data:</h1> <ul> <li v-for="(data, index) in dataList" :key="index">{{ data }}</li> </ul> <input type="text" v-model="message" /> <button @click="send">Send</button> </div> </template> <script> export default { data() { return { dataList: [], message: '' } }, mounted() { const ws = new WebSocket('ws://localhost:8080/ws'); ws.onmessage = (event) => { this.dataList.push(event.data); }; ws.onclose = () => { console.log('Connection closed'); }; }, methods: { send() { const ws = new WebSocket('ws://localhost:8080/ws'); ws.onopen = () => { ws.send(this.message); ws.close(); }; } } } </script> ``` 在这个示例中,我们首先创建了一个 WebSocket 服务端,其中包含了一个用于处理客户端发送消息的方法 send(),它会将接收到的消息发送给所有连接上的客户端。我们还创建了一个 WebSocketService,用于管理客户端连接和消息发送。 在前端,我们通过 Vue 的 mounted 生命周期创建了一个 WebSocket 连接,并在每次接收到服务器发来的消息时将其添加到一个数据列表中,然后在模板中通过 v-for 渲染出来。我们还在模板中添加了一个文本框和一个按钮,用于向服务器发送消息。当用户点击按钮时,我们创建一个新的 WebSocket 连接,并在连接打开后发送用户输入的消息,然后立即关闭连接。 要注意的是,我们在前端中创建了两个 WebSocket 连接,一个用于接收服务器推送的数据,另一个用于向服务器发送数据。这是因为 WebSocket 是全双工通信,可以同时进行发送和接收操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值