0


运维相关(一) - Vue项目配置WebSocket连接{ws、wss 连接方式}

Vue项目配置WebSocket连接 ws、wss 两种方式

1. 写作背景

  • 项目使用的是ruoyi的前后端分离框架
  • 项目需要使用到 websocket , 在本地使用 ws 连接方式是没问题 , 但是服务器上边使用的是nginx + ssl 证书 https域名访问的方式部署的 使用普通的 ws 连接是不可以成功的 需要使用 wss的方式

2. 晒出代码

2.1 前端 vue.config.js 的代码

  • 这里target: 里边指向的都是后端server的地址 16000是我后端服务的端口 , 我这里websocket服务和普通的业务项目用的都是一个项目 所以都是16000端口

devServer:{host:'0.0.0.0',port: port,open:true,proxy:{// detail: https://cli.vuejs.org/config/#devserver-proxy// 正常的 http 请求代理[process.env.VUE_APP_BASE_API]:{target:`http://localhost:16000`,changeOrigin:true,pathRewrite:{['^'+ process.env.VUE_APP_BASE_API]:''}},// websocket ws 的代理路由配置[process.env.VUE_APP_WEBSOCKET_API]:{target:`ws://localhost:16000`,changeOrigin:true,ws:true,pathRewrite:{['^'+ process.env.VUE_APP_WEBSOCKET_API]:''}},// websocket wss 的代理路由配置[process.env.VUE_APP_WSS_WEBSOCKET_API]:{target:`wss://域名:16000`,changeOrigin:true,ws:true,pathRewrite:{['^'+ process.env.VUE_APP_WSS_WEBSOCKET_API]:''}}},disableHostCheck:true},

2.2 Vue项目路由配置代码

  • 为什么要配置两个地址呢? , 因为在本次测试的时候使用的是普通的ws方式连接 所以为了方便切换就写了两个websocket代理路由

  • .env.development 文件和 .env.production 文件都加上这两行代码即可
//  WebSocket地址VUE_APP_WEBSOCKET_API='/websocket-api'// WebSocket wss 地址VUE_APP_WSS_WEBSOCKET_API='/wss-websocket-api'

3.3 服务器Nginx配置

server {
        add_header X-Frame-Options ALLOWALL;
        listen       8681 ssl;
        server_name 域名; #需要将yourdomain.com替换成证书绑定的域名。
        root ..\html;
        index index.html index.htm;
        ssl_certificate pem文件地址;  #需要将cert-file-name.pem替换成已上传的证书文件的名称。
        ssl_certificate_key文件地址; #需要将cert-file-name.key替换成已上传的证书私钥文件的名称。
        ssl_session_timeout 6m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #表示使用的TLS协议的类型。
        ssl_prefer_server_ciphers on;
        #charset koi8-r;

        access_log  logs/host.access.log  main;

        #默认目录
        location / {
            root   C:/xxx/dist;
            index  index.html;
            try_files $uri $uri/ @router;
        }
        location @router {
            rewrite ^.*$ /index.html last;
        }

        #vue二级目录代理
        location /admin {
            alias /root/www/admin;
            index  index.html;
            try_files $uri $uri/ /index.html last;
        }

        location /prod-api {
             rewrite  ^/prod-api/(.*)$ /$1 break;
            proxy_pass http://localhost:16000;
            proxy_set_header Host $host;
            add_header X-Frame-Options ALLOWALL;
            proxy_set_header User-Agent $http_user_agent;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header authorization $http_authorization;
        }
        # websocket  wss 连接方式的路由代理配置
        location /wss-websocket-api {
             rewrite  ^/wss-websocket-api/(.*)$ /$1 break;
             proxy_pass http://localhost:16000;        #通过配置端口指向部署websocker的项目
             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;
         }
        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }

3. 使用方式

3.1 前端代码

// 当前浏览器Location对象const nowLocation = window.location;// 协议 => http、httpsconst protocol = nowLocation.protocol;// hostName => ipconst hostName = nowLocation.hostname;// host => ip:portconst host = nowLocation.host;// websocket api 地址// 这个判断就是根据当前项目环境 自动确定使用 ws 还是 wss 的路由地址const websocket_pattern =(hostName =='域名')?'wss-websocket-api':'websocket-api';// websocket 请求地址前缀const webSocketApiUrl =((protocol.startsWith('https'))?'wss://':'ws://')+ host +'/'+ websocket_pattern;// 当前WebSocket的请求地址前缀,// /websocket/template-push/ 就是我后端配置的websocket端点地址letREQUEST_WEBSOCKET_URL_PREFIX= webSocketApiUrl +'/websocket/template-push/';// 当前的WwebSocket对象letCURRENT_SOCKET=null;// 当前请求WebSocket的指令代码letCURRENT_INDICATE_CODE=null;letENABLE_CONFIG={WEBSOCKET_PUSH_VIDEO_ENABLE:true,}/**
 * 1. 初始化WebSocket连接对象
 * @param {*} clientKey 当前客户端Key
 */functionopenWebSocket(clientKey){if(CURRENT_SOCKET!=null){CURRENT_SOCKET.close();CURRENT_SOCKET=null;}CURRENT_SOCKET=newWebSocket(REQUEST_WEBSOCKET_URL_PREFIX+ clientKey);CURRENT_SOCKET.onopen=event=>{
        console.log('连接Socket');};// 从服务器接受到信息时的回调函数CURRENT_SOCKET.onmessage=event=>{
        console.log('收到服务器响应 , 响应数据信息==>', event.data);};CURRENT_SOCKET.onclose=event=>{
        console.log('关闭Socket连接!');};//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload=()=>{CURRENT_SOCKET.close();CURRENT_SOCKET=null;};}functiongetWebSocketConnection(){returnCURRENT_SOCKET;}
  • 前端websocket向后端发送数据使用方式
let sendData ={};getWebSocketConnection().send(JSON.stringify(sendData));

3.2 后端代码

packagecom.ruoyi.web.controller.websocket;importcom.alibaba.fastjson2.JSONException;importcom.alibaba.fastjson2.JSONObject;importcom.ruoyi.common.utils.StringUtils;importcom.ruoyi.websocket.WebSocketClientIndicate;importcom.ruoyi.websocket.WebSocketRequest;importcom.ruoyi.websocket.WebSocketTemplateSession;importlombok.RequiredArgsConstructor;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;importjavax.websocket.CloseReason;importjavax.websocket.OnClose;importjavax.websocket.OnMessage;importjavax.websocket.OnOpen;importjavax.websocket.Session;importjavax.websocket.server.PathParam;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.atomic.LongAdder;/**
 * --->
 *
 * @author xqh , [email protected]
 * @data 2023-11-02 15:46:51
 */@Component@ServerEndpoint("/websocket/template-push/{clientKey}")@RequiredArgsConstructorpublicclassWebSocketTemplateInfoPushServer{/**
     * 统计在线人数 线程安全的计数器 比原子更新类 效率更高 更专业
     */privatefinalstaticLongAdderONLINE_ADDER=newLongAdder();/**
     * 客户端 连接会话存储Map , 每个客户端对应一个唯一Id , 在当前端点中 唯一Id为Session Id
     */privatefinalstaticMap<String,WebSocketTemplateSession>SESSION_MAP=newConcurrentHashMap<>();/**
     * 通过 clientKey 反查 sessionId , key为clientKey , value 为sessionId
     */privatefinalstaticMap<String,String>CLIENT_KEY_SESSION_STORE_MAP=newConcurrentHashMap<>();privatestaticfinalLoggerWEBSOCKET_TEMPLATE_PUSH_LOGGER=LoggerFactory.getLogger(WebSocketTemplateInfoPushServer.class);/**
     * 1. 有新的连接访问当前 websocket 地址
     *
     * @param session      当前客户端的服务器对象 session
     * @param clientCode   客户端设备唯一code码
     */@OnOpenpublicvoiddoConnectionSocket(Session session,@PathParam("clientKey")String clientCode){// 前端异常 通过抓包发送 则直接关闭当前创建的session对象if(StringUtils.isEmpty(clientCode)){try{
                session.close(newCloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT,"参数不合法,已关闭当前连接!"));}catch(IOException e){WEBSOCKET_TEMPLATE_PUSH_LOGGER.error(e.getMessage());}}// 正常则建立连接 存储数据 并返回连接成功else{String sessionId = session.getId();SESSION_MAP.put(sessionId,newWebSocketTemplateSession(session,clientCode));CLIENT_KEY_SESSION_STORE_MAP.put(sessionId,clientCode);ONLINE_ADDER.increment();WEBSOCKET_TEMPLATE_PUSH_LOGGER.info("WebSocket-连接成功,此刻连接设备码为: [{}] , 此刻在线的连接数为:[{}]", clientCode,ONLINE_ADDER.sum());}}/**
     * 2. 关闭当前websocket连接
     * @param session
     */@OnClosepublicvoiddoCloseSocket(Session session){try{String sessionId = session.getId();WebSocketTemplateSession doCloseSession =SESSION_MAP.get(sessionId);
            doCloseSession.getSession().close();// 清除当前关联的Session信息SESSION_MAP.remove(sessionId);CLIENT_KEY_SESSION_STORE_MAP.remove(sessionId);ONLINE_ADDER.decrement();WEBSOCKET_TEMPLATE_PUSH_LOGGER.info("WebSocket-连接关闭,此刻在线的连接数为:[{}]",ONLINE_ADDER.sum());}catch(IOException e){WEBSOCKET_TEMPLATE_PUSH_LOGGER.error(e.getMessage());}}/**
     * 3. 接收客户端主动发送的消息数据
     * @param session       当前会话
     * @param jsonMessage   客户端发送的JSON数据
     */@OnMessagepublicvoidreceiveMessage(Session session ,String jsonMessage){try{// 收到前端发送的信息}catch(JSONException jsonException){WEBSOCKET_TEMPLATE_PUSH_LOGGER.error("JSON格式有误,异常信息->[{}]", jsonException.getMessage());}catch(Exception e){WEBSOCKET_TEMPLATE_PUSH_LOGGER.error("接收信息接口失败,异常信息->[{}]", e.getMessage());}}}
  • WebSocketTemplateSession
@Data@AllArgsConstructorpublicclassWebSocketTemplateSession{privateSession session;privateString clientKey;}

4. 测试使用

  • 本地的 ws 方式在这里插入图片描述
  • 服务器的 wss 方式在这里插入图片描述
标签: 运维 vue.js websocket

本文转载自: https://blog.csdn.net/weixin_44321065/article/details/134314213
版权归原作者 ~奇思妙想的王多鱼 所有, 如有侵权,请联系我们删除。

“运维相关(一) - Vue项目配置WebSocket连接{ws、wss 连接方式}”的评论:

还没有评论