【Web通信】WebSocket详解:WebSocket是什么?如何使用WebSocket?在Vue中封装WebSocket(心跳监测)。nginx配置websocket。

23 篇文章 3 订阅
5 篇文章 0 订阅

一、WebSocket相关定义

1. WebSocket定义

WebSocket 是一种基于TCP全双工通信协议,它提供了一种在浏览器和服务器之间建立持久连接来交换数据的方法。数据可以作为“数据包”在两个方向上传递,而无需中断连接也无需额外的 HTTP 请求。

  • 使用场景:对于需要连续数据交换的服务,例如网络游戏,实时交易系统等,WebSocket 尤其有用。最典型的场景就是聊天室。
    在这里插入图片描述

2. WebSocket产生原因

HTTP 协议有一个缺陷:通信只能由客户端发起。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法----所以WebSocket 就是应运而生。

根据信息的传送方向,【串行通信】可以进一步分为单工半双工全双工三种。
信息只能单向传送为"单工";
信息能双向传送但不能同时双向传送称为"半双工";
信息能够同时双向传送则称为"全双工"。

二、WebSocket 的使用

1. 创建链接

WebSocket 对象作为一个构造函数,用于新建 WebSocket实例。
我们还需要在 url 中使用特殊的协议 ws 创建实例:

let ws = new WebSocket("ws://javascript.info");

执行上面语句之后,客户端就会与服务器进行连接。

它同样也有一个加密的 wss:// 协议。类似于 WebSocket 中的HTTPS。(具体见本文下方第四节:nginx配置websocket)
类比如下图:
在这里插入图片描述
在这里插入图片描述

2. WebSocket 的事件

一旦 WebSocket 连接被建立,我们就应该监听实例上的事件。一共有 4 个事件:

  • onopen - 建立连接时触发
  • onmessage - 客户端接受服务端数据时触发
  • onerror - 通信错误时触发
  • onclose - 连接关闭时触发

3. WebSocket 的方法

  • send(body) - 向服务器发送文本或二进制数据:调用允许 body 是字符串或二进制格式,包括 Blob,ArrayBuffer 等。不需要额外的设置:直接发送它们就可以了。
  • close() - 关闭连接

4. WebSocket.readyState

readyState 属性返回实例对象的当前状态,共有四种状态

  • CONNECTING: 值为0 - 表示正在连接,连接尚未建立
  • OPEN:值为1 - 表示连接成功,可以进行通信
  • CLOSING:值为2 - 表示连接正在关闭
  • CLOSED:值为3 - 表示连接已经关闭,或者打开连接失败

5. 简单示例

// 创建一个WebSocket对象-接口地址如"ws://javascript.info"
var ws = new WebSocket("接口地址")

// 连接成功时触发
ws.onopen = function() {
    alert("连接成功")
}
// 连接失败时触发
ws.onerror = function() {
    alert("连接失败")
}
// 发送数据
ws.send("这是文本或者二进制数据"); // 向服务端发送请求

// 接收消息时触发
ws.onmessage = function(MessagEvent) {
    console.log(MessagEvent.data)
}
// 连接关闭的回调函数
ws.onclose = function(){
	alert("close"}

三、在Vue中封装WebSocket

WebSocket心跳机制:
在使用websocket过程中,可能会出现网络断开的情况,比如信号不好,或者网络临时性关闭,
这时候websocket的连接已经断开,而浏览器不会执行websocket onclose方法,我们无法知道是否断开连接,也就无法进行重连操作。

基于以上问题:如果当前发送websocket数据到后端,一旦请求超时,onclose便会执行,这时候便可进行绑定好的重连操作—这就是WebSocket心跳机制

心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了,需要重连。

自己封装的socket.ts:

/*
 * 这里的封装export了三个方法
 * initWebSocket 用来初始化websock,可传入url
 * sendWebsocket 用来发送数据
 * closeWebsocket 用来关闭连接
*/

import { ElMessage } from 'element-plus';
// import { Session } from '/@/utils/storage'; // 引入自定义存储方法

interface IWebSocket {
	url: string; // webSocket地址
	heartbeatTimeCycle?: number; // 心跳周期时间
}

let ws:any = null; // webSocket实例
let socketUrl = "wss://javascript.info/article/websocket/demo/hello";

let lockReconnect = false; // 是否真正建立连接
let heartbeatTimeCycle:number = 20 * 1000; // 定义心跳周期默认为20s
let timeoutObj:any = null; //心跳心跳倒计时
let serverTimeoutObj:any = null; //心跳倒计时
let timeoutNum:any = null;
let global_callback:any = null;

/**
 * @description: 初始化webSocket
 * @param {string} url 传入的ws链接,例如"wss://javascript.info/article/websocket/demo/hello"
 */
export const initWebSocket = (params:IWebSocket) => {
  socketUrl = params.url; // socket地址
  heartbeatTimeCycle = params.heartbeatTimeCycle || 20000; // 心跳周期

  if (!window.WebSocket) {
    ElMessage.error({
        message: "您的浏览器不支持websocket,请升级或更换浏览器!",
        type: "error",
        center: true,
    });
    return;
  }
  if (!ws) {
    ws = new WebSocket(socketUrl); // 实例化WebSocket对象为ws
    socketOnOpen();   // 开启链接
    socketOnClose();  // 监听webSocket是否关闭
    socketOnError();  // 监听webSocket是否出现错误,如果出现则重连
    socketOnMessage(); // 监听后端消息
  }
}

const socketOnOpen = () => {
  ws.onopen = () => {
    console.log("socket连接成功");
    // 开启心跳
    startHeartbeat();
  };
}

const socketOnClose = () => {
  ws.onclose = () => {
    console.log("socket已经关闭");
  };
}

/**
 * @description: 向后端发送webSocket数据
 * @param {any} data 字符串或二进制格式
 */
const socketOnSend = (data:any) => {
  // 如果连接正常
  if (ws.readyState == 1) {
    ws.send(data);
  }
}

const socketOnError = () => {
  ws.onerror = () => {
    reconnect();
    console.log("socket链接错误");
  };
}

// 监听后端返回的信息
const socketOnMessage = () =>  {
  ws.onmessage = (event:any) => {
    if(event.data.includes("heartbeat")){
      // 在心跳周期内收到后端消息, 则重置心跳
      resetHeartbeat();
    }else{
      // 把后端返回值给回调函数
      console.log("来自后端的数据---",event.data);
      global_callback(event.data);
    }
  };
}

// 开启心跳
const startHeartbeat = () => {
  timeoutObj && clearTimeout(timeoutObj);
  serverTimeoutObj && clearTimeout(serverTimeoutObj);
  console.log("开启心跳---heartbeatTimeCycle心跳周期:(毫秒)",heartbeatTimeCycle);

  timeoutObj = setTimeout(() => {
    // 如果连接正常
    if (ws.readyState == 1) {
        // 这里发送一个心跳,后端收到后,返回一个心跳消息,
        ws.send('{"heartbeat":"发送给后端的心跳heartbeat"}');
    } else {
        // 否则重连
        reconnect();
    }
    serverTimeoutObj = setTimeout(() => {
        // 超时关闭
        console.log("心跳超时未响应---",heartbeatTimeCycle);
        ws.close();
    }, heartbeatTimeCycle);
  }, heartbeatTimeCycle);
}

//重置心跳
const resetHeartbeat = () =>  {
  //清除时间
  clearTimeout(timeoutObj);
  clearTimeout(serverTimeoutObj);
  //重启心跳
  startHeartbeat();
}

// 断线重连
const reconnect  = () =>  {
  if (lockReconnect) {
    return;
  }
  lockReconnect = true;
  //没连接上会一直重连,设置延迟避免请求过多
  timeoutNum && clearTimeout(timeoutNum);
  timeoutNum = setTimeout(() => {
    //新连接
    initWebSocket({
      url: socketUrl,
      heartbeatTimeCycle: 20
    });
    lockReconnect = false;
  }, 5000);
}

export const sendWebsocket = (agentData:any, callback:any) => {
  global_callback = callback;
  socketOnSend(agentData);
}

/**
 * @description: 前端主动关闭链接
 */
export const closeWebsocket = () => {
  if (ws) {
    ws.close();
  }
  clearTimeout(timeoutObj);
  clearTimeout(serverTimeoutObj);
}

其他地方调用:

注意调用时机,与页面取消挂载时也需要关闭websocket链接

import { initWebSocket, sendWebsocket } from '/@/utils/screen/socket';

。。。其他代码

// 页面挂载时
onMounted(() => {
  // socket
  initWebSocket({
    url: "wss://javascript.info/article/websocket/demo/hello",//"ws://10.6.60.165:10051/websocket", 
    heartbeatTimeCycle: 6000, // 定义心跳周期默认为5s
  });
  sendWebsocket("发送的数据",sentMsg);
});
// 回调函数
const sentMsg = (e:any) => {
  console.log(e);
  if(e.includes("refresh_workOrder_bigScreen")){
    let echartsMapRef = document.getElementById('echartsMap');
    setTimeout(() => {
      initEchartsMap(echartsMapRef);
    }, 1000);
  }
};

// 页面取消挂载时
onBeforeUnmount(() => {
  if (!state.chart) {
    return
  }
  // 销毁chart实例
  state.chart.dispose(); 
  state.chart = null;
  // 关闭webSocket链接
  closeWebsocket();
});

界面接口效果:

在这里插入图片描述

在这里插入图片描述

四、nginx配置websocket

表明是websocket连接进入的时候,进行一个连接升级将http连接变成websocket的连接。

http {
	server {
        listen       80;
        server_name  zhyl.whhksj.com.cn;
      
        location / {
            # 所有GET请求直接301跳转不用管,非GET请求的用proxy_pass来转发,将参数传递给服务
            if ($request_method ~ ^(POST|DELETE|OPTIONS)$) {
                proxy_pass https://zhyl.whhksj.com.cn;
                break ;
            }
		
            #把http的域名请求转成https
            rewrite ^(.*)$ https://${server_name}$1 permanent;
        }
      
	    #配置wss开始 ---------- 走http请求,前端需要用 ws://zhyl.whhksj.com.cn/websocket进行连接 ^~ 代表通配符
        location ^~ /websocket  {
			proxy_pass http://123.60.20.985:10051/websocket;
	
			proxy_set_header X-Real-IP $remote_addr;
			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
			proxy_set_header Host $host;
			proxy_set_header X-NginX-Proxy true;
			proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection "Upgrade";
			proxy_connect_timeout 600s;
			proxy_read_timeout 600;
			proxy_send_timeout 600s;
        }
        #配置wss结束------------------------------------------
    }
        

    #nginx证书配置 https://cloud.tencent.com/document/product/400/35244
    server {
            #SSL 默认访问端口号为 443
            listen 443 ssl; 
            #请填写绑定证书的域名
            #server_name zhyl.whhksj.com.cn; 
            #请填写证书文件的相对路径或绝对路径
            ssl_certificate /etc/pki/tls/certs/nginx/whhksj.com.cn.pem; 
            #请填写私钥文件的相对路径或绝对路径
            ssl_certificate_key /etc/pki/tls/certs/nginx/whhksj.com.cn.key; 
            ssl_session_timeout 5m;
            #请按照以下协议配置
            ssl_protocols TLSv1.2 TLSv1.3; 
            #请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
            ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 
            ssl_prefer_server_ciphers on;  
            #静态资源访问配置
    	    server_name  localhost;
    		root   /usr/local/webapp/after-sales-cloud;
    		keepalive_timeout 600;        

            #charset koi8-r;

            #access_log  logs/host.access.log  main;

            location / {
                index  index.html index.htm;
    			add_header Access-Control-Max-Age 86400;
    		    add_header Pragma no-cache;
    		    add_header Expires 0;
            }
    		
    		#解决前端跨域请求问题 80到10051
            location /api {
                proxy_pass http://123.60.20.985:10051;
            }

            location /wx {
                proxy_pass http://123.60.20.985:10051;
            }
    		
    		location /public {
                proxy_pass http://123.60.20.985:10051;
            }
            
    	    location /data/wx/wx_files/ {
                alias /data/wx/wx_files/;
            }
    	
    	    location /data/pc/pc_files/ {
                alias /data/pc/pc_files/;
            }
    	
            location /login {
               try_files $uri /index.html;
            }

            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }

            #配置wss开始 ---------- 
            #走wss请求,前端需要用 wss://zhyl.whhksj.com.cn/websocket 进行连接 
            #两个位置都配置 则前端通过ws/wss均可建立连接      ^~ 代表通配符
            location ^~ /websocket {
    			proxy_pass http://123.60.20.985:10051/websocket;
    	
    			proxy_set_header X-Real-IP $remote_addr;
    			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    			proxy_set_header Host $host;
    			proxy_set_header X-NginX-Proxy true;
    			proxy_http_version 1.1;
    			proxy_set_header Upgrade $http_upgrade;
    			proxy_set_header Connection "Upgrade";
    			proxy_connect_timeout 600s;
    			proxy_read_timeout 600;
    			proxy_send_timeout 600s;
           }
           #配置wss结束------------------------------------------

    }
}

📢欢迎------点赞👍/ 收藏⭐/ 留言📝------如有错误敬请指正!

后续持续更新中…

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
nginx部署vue使用websocket时,你需要进行一些配置。首先,在开发环境,你可以在vue.config.js文件配置devServer,使用proxy来代理websocket请求。具体的配置可以参考以下代码示例: ``` devServer: { proxy: { '/wsct': { target: '真正开发环境的websocket地址', changeOrigin: true, pathRewrite: { '^/wsct': '', }, ws: true } } } ``` 在线上环境,你需要在nginx配置添加一些规则,以支持websocket请求。你可以参考以下代码示例: ``` server { location /wsct { proxy_pass '线上websocket地址'; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` 这样,你就可以在vue项目使用websocket进行通信了。另外,如果你想进行群聊,可以使用不同的访问地址来区分不同的用户,例如ws://localhost:80/ws/socket/lhz/和ws://localhost:80/ws/socket/zzh/。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue项目部署在nginxvue开发环境代理websocket请求,线上nginx代理websocket请求。](https://blog.csdn.net/weixin_43790802/article/details/127285321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [django部署:django+uwsgi+nginx+vue+websocket](https://blog.csdn.net/weixin_46371752/article/details/130568190)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [websocket消息推送_windows+nginx+wss+https.rar](https://download.csdn.net/download/qq_31648519/12596783)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卸载引擎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值