SpringBoot下WebSocket+React例子

1、Java端

1.1、引入SpringBoot的WebSocket包,Maven配置:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

1.2、增加WebSocket配置类

package com.tfe.sell.common.config;

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


/**
 * 开启WebSocket支持
 */
@Configuration  
public class WebSocketConfig {  
    
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
} 

1.3、添加WebSocket的服务类

package com.tfe.sell.common.config;

import com.alibaba.fastjson.JSON;
import com.tfe.sell.common.entity.JsonResult;
import com.tfe.sell.common.entity.WebSocketJsonResult;
import com.tfe.sell.common.enumeration.WebSocketMessageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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("/admin/websocket/{userid}")
@Component
public class WebSocketServer {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

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

    //接收sid
    private String userid = "";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam("userid") String userid) {
        if (!WebSocketServer.contains(userid)){
            this.session = session;
            webSocketSet.add(this);     //加入set中
            addOnlineCount();           //在线数加1
            logger.info("有新窗口开始监听:" + userid + ",当前在线人数为" + getOnlineCount());
            this.userid = userid;
        }
    }

    public static Boolean contains(String userid){
        for (WebSocketServer item : webSocketSet) {
            if (item.userid.equals(userid)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        logger.info("有一连接关闭["+ this.userid +"],当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("收到来自窗口" + userid + "的信息:" + message);
        WebSocketJsonResult jsonResult = JSON.parseObject(message,WebSocketJsonResult.class);
        handleMessage(jsonResult);
    }

    public void handleMessage(WebSocketJsonResult jsonResult){
        WebSocketMessageType messageType = WebSocketMessageType.getByCode(jsonResult.getCode());
        if (messageType == null){
            logger.error("传入的类型不正确",jsonResult.getCode());
            return;
        }
        switch (messageType){
            case clientHeartbeat:
                WebSocketJsonResult newMessage = new WebSocketJsonResult(null,
                        WebSocketMessageType.clientHeartbeatReply.getCode(),
                        WebSocketMessageType.clientHeartbeatReply.getName());
                String jsonString = JSON.toJSONString(newMessage);
                sendInfo(jsonString,userid);
                break;
            default:
                //不做任何事情
                logger.error("传入的类型没有对应的处理");
                break;
        }
    }

    /**
     * 
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("发生错误" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,String userid) {
        logger.info("推送消息到窗口" + userid + ",推送内容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的,为null则全部推送
                if(userid == null) {
                    item.sendMessage(message);
                }else if(item.userid.equals(userid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                logger.error("error:" + e.getMessage());
                continue;
            }
        }
    }

    public static Integer getSize(){
        return webSocketSet.size();
    }

    public static String getUserIdList(){
        StringBuilder sb = new StringBuilder();
        for (WebSocketServer item : webSocketSet) {
            sb.append(item.userid);
            sb.append(",");
        }
        return sb.toString();
    }

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

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

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

这个服务类用到一些实体和枚举:

WebSocketJsonResult:

package com.tfe.sell.common.entity;

/**
 * 
 * JSON模型
 * 
 * 用户后台向前台返回的JSON对象
 *
 * 
 */
public class WebSocketJsonResult implements java.io.Serializable {

    private static final long serialVersionUID = -1118025395225258944L;

    private String message = "";

    private int code = 1;

    private Object result = null;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object obj) {
        this.result = obj;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public WebSocketJsonResult(){}

    public WebSocketJsonResult(Object result, int code, String message){
        this.result = result;
        this.code = code;
        this.message = message;
    }

    public static WebSocketJsonResult successInstance(){
        return new WebSocketJsonResult(null,1,"成功");
    }

    public static WebSocketJsonResult failInstance(String message){
        return new WebSocketJsonResult(null,-1,message);
    }

}

 

2.React端

2.1、添加WebSocket组件

新建WebSocket目录,添加Index.js,代码如下:

/**
 * 参数:[socketOpen|socketClose|socketMessage|socketError] = func,[socket连接成功时触发|连接关闭|发送消息|连接错误]
 * timeout:连接超时时间
 * @type {module.webSocket}
 */
module.exports =  class webSocket {
  constructor(param = {}) {
    this.param = param;
    this.reconnectCount = 0;
    this.socket = null;
    this.taskRemindInterval = null;
    this.isSucces=true;
  }
  connection = () => {
    let {socketUrl, timeout = 0} = this.param;
    // 检测当前浏览器是什么浏览器来决定用什么socket
    if ('WebSocket' in window) {
      console.log('WebSocket');

      this.socket = new WebSocket(socketUrl);
    }
    else if ('MozWebSocket' in window) {
      console.log('MozWebSocket');

      this.socket = new MozWebSocket(socketUrl);
    }
    else {
      console.log('SockJS');

      this.socket = new SockJS(socketUrl);
    }
    this.socket.onopen = this.onopen;
    this.socket.onmessage = this.onmessage;
    this.socket.onclose = this.onclose;
    this.socket.onerror = this.onerror;
    this.socket.sendMessage = this.sendMessage;
    this.socket.closeSocket = this.closeSocket;
  };
  // 连接成功触发
  onopen = () => {
    let {socketOpen} = this.param;
    this.isSucces=false;  //连接成功将标识符改为false
    socketOpen && socketOpen();
  };
  // 后端向前端推得数据
  onmessage = (msg) => {
    let {socketMessage} = this.param;
    socketMessage && socketMessage(msg);
  };
  // 关闭连接触发
  onclose = (e) => {
    this.isSucces = true;   //关闭将标识符改为true
    this.socket.close();
    let {socketClose} = this.param;
    socketClose && socketClose(e);
  };
  onerror = (e) => {
    // socket连接报错触发
    let {socketError} = this.param;
    this.socket = null;
    socketError && socketError(e);
  };
  sendMessage = (value) => {
    // 向后端发送数据
    if(this.socket) {
      this.socket.send(JSON.stringify(value));
    }
  };
  closeSocket = () => {
    this.socket.close();
  };
  //获得状态
  readyState = () => {
    return this.socket.readyState;
  }
};

2.2 在需要触发服务的页面,例如订单管理页面增加WebSocket引用代码

import WebSocket from "components/WebSocket";

socket = null;
@observable
webSockethadOpen = false;
//重试次数
websocketConnectedCount = 0;


openWebSocket = () => { let head = constantStore.ROOT_API_URL.replace("https","wss").replace("http","ws"); let url = head + "/admin/websocket/" + userStore.id; this.socket = new WebSocket({ socketUrl: url, socketMessage: (msg) => { var myMessage = msg; var data = myMessage.data; if (data != undefined){ let jsonResult = JSON.parse(data); if (jsonResult.code == 101) { console.info("收到新订单消息",jsonResult); let orderGroupId = jsonResult.result; this.soundNotice(); Modal.confirm({ title: "新订单", content: "有新订单,是否显示?", okText: "确认", cancelText: "取消", onOk: () => { if(this != undefined){ this.refs["DetailModal"].showModal(orderGroupId); } else{ message.error("请先打开【订单管理】页面"); } } }); } else if (jsonResult.code == 401){ //401是后端收到心跳包,并返回的消息类型 console.info("收到后台回复心跳包",jsonResult); this.heartCheck.reset().start(); // 如果获取到消息,说明连接是正常的,重置心跳检测 } } }, socketClose: (msg) => { this.webSockethadOpen = false; message.success("订单监控已关闭"); console.info("订单监控已关闭"); }, socketError: () => { console.info("连接建立失败"); this.webSockethadOpen = false; this.websocketConnectedCount++; if(this.websocketConnectedCount <= 5){ message.error('连接建立失败,尝试重连'); this.openWebSocket(); } else{ message.error('连接建立失败,请联系管理员'); } }, socketOpen: () => { message.success("开始订单监控"); console.info("开始订单监控"); this.heartCheck.reset().start(); // 成功建立连接后,重置心跳检测 } }); //重试创建socket连接 try { this.socket.connection(); } catch (e) { // 捕获异常,防止js error } this.webSockethadOpen = true; }; closeWebSocket = () => { if (this.webSockethadOpen == true){ this.socket.closeSocket(); } }; soundNotice = () => { var url = "/new_order.mp3"; var audio = new Audio(url); audio.src = url; audio.play(); }; componentDidMount() { //初始化心跳包对象 this.initHeartCheck(); //进入自动开启监控 this.openWebSocket(); } //初始化心跳包对象 initHeartCheck = () => { let _this = this; // 心跳检测, 每隔一段时间检测连接状态,如果处于连接中,就向server端主动发送消息,来重置server端与客户端的最大连接时间, // 如果已经断开了,发起重连。 this.heartCheck = { timeout: 10 * 60 * 1000, //10分钟发送一次心跳包,输入毫秒数 serverTimeoutObj: null, reset: function () { if (this.serverTimeoutObj != undefined){ clearTimeout(this.serverTimeoutObj); } return this; }, start: function () { this.serverTimeoutObj = setInterval(function () { if (_this.webSockethadOpen) { if (_this.socket.readyState() == 1) { var message = { "code":301, "message":"发送心跳包" }; console.log("发送心跳包消息",message); _this.socket.sendMessage(message); _this.heartCheck.reset().start(); // 如果获取到消息,说明连接是正常的,重置心跳检测 } else { console.log("断开状态,尝试重连"); _this.openWebSocket(); } } }, this.timeout) } }; }; componentWillUnmount(){ console.info("componentWillUnmount"); this.closeWebSocket(); }

<Button type="primary" disabled={this.webSockethadOpen} onClick={() => this.openWebSocket()} >
监听订单
</Button>
<Button type="primary" disabled={!this.webSockethadOpen} onClick={() => this.closeWebSocket()}>
关闭监听
</Button>
 

 

posted @ 2019-09-05 08:45  门罗的魔术师  阅读(2195)  评论(4编辑  收藏  举报