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

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

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

(1)websocket依赖


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-websocketartifactId>
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/参数/参数

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()">运行 WebSocketa>
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";
			}
		  }

你可能感兴趣的:(java,springboot,spring,boot,websocket)