Vue 中如何使用 WebSocket 以及 Nginx 代理相关配置

5,937 阅读2分钟

简述websocket特性:

1.主要有ws(不加密)和wss(加密);ws是基于http请求建立握手,wss是基于https请求建立握手。
2.ws的握手基于httpget方式,协议应不小于1.1。
3.wswss的请求头会比单纯的http的请求头多很多特殊的header
4.ws请求在建立连接后,通信双方都可以在任何时刻向另一方发送数据。(http只能客户端发送请求给服务端)
websocket的基本用法这里就不介绍了,网上相关的文章有很多,在此贴出不错的链接地址。
w3cwebsocket的介绍:www.runoob.com/html/html5-…

项目配置:

// 修改vue.config.js文件
  devServer: {
    open: true, // 启动后在浏览器打开
    proxy: { 
      '/api': { // 设置普通的http代理
        target: 'http://x.x.x.x:8080',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      },
      '/socket': { // 设置websocket代理
        target: 'http://x.x.x.x:8080',
        ws: true, // 开启websocket代理  注意
        changeOrigin: true,
        pathRewrite: {
          '^/socket': ''
        }
    }}}

Vue单页面

export default {
  data () {
    return {
      socket: null,
      connectCount: 0, // 连接次数
      heartInterval: null
    }
  },
  created () {
    this.initSocket()
  },
  methods: {
    /**
     * 建立连接是http
     * 消息推送都是tcp连接,没有同源限制
     * 服务端人员try catch 消息推送不成功,则关闭连接
     */
    initSocket () {
      const { protocol, host } = location
      const url = `${protocol === 'https' ? 'wss' : 'ws'}://${host}/socket/websocket/mineStatus/${this.$store.getters.userId}`
      // 判断当前浏览器是否支持WebSocket
      if (typeof WebSocket === 'undefined') {
        this.$notification.warning({
          message: '系统提示',
          description: '您的浏览器不支持socket',
          duration: 4
        })
        return
      }
      
      this.socket = new WebSocket(url)
      this.socket.onmessage = (evt) => {
        if (evt.data === '连接成功' || evt.data.includes('refresh')) {
          this.heartCheck() // 重置心跳检测
          // this.onRefresh() 接收到推送消息,刷新列表
        }
      }
      
      // 监听窗口事件,当窗口关闭时,主动断开websocket连接
      window.onbeforeunload = () => {
        this.closeSocket()
      }
    },
    /**
     * 定时发送心跳包
     * 59s发送一次心跳,比nginx设置的最大连接时间短一点,以达到在临界点重置连接时间
     */
    heartCheck () {
      const _this = this
      this.heartInterval && clearTimeout(this.heartInterval)
      this.heartInterval = setInterval(() => {
        if (this.socket.readyState === 1) { // 连接状态
          this.socket.send('ping')
        } else {
          _this.connectCount += 1
          if (_this.connectCount <= 5) {
            this.initSocket() // 断点重连5次
          }
        }
      }, 59 * 1000)
    },
    /** 断开websocket连接 */
    closeSocket() {
      this.socket.close()
      this.heartInterval && clearTimeout(this.heartInterval)
    }
  },
  destroyed() {
    this.closeSocket()
  }
}

ps:当客户端网络不好,或者断网,服务器并不知情,还是会给客户端推送消息,造成资源浪费,故后端服务器要做异常处理,在消息推送不成功,主动关闭websocket连接。

Nginx配置

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        listen       8084;
        server_name  _;
        root         /usr/share/nginx/html/pcNet;
        include /etc/nginx/default.d/*.conf;
        
        location ^~/socket/ {
           proxy_pass http://x.x.x.x:8080/;
           proxy_http_version 1.1; 
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           proxy_set_header Upgrade websocket;
           proxy_set_header Connection Upgrade;
        }        

        location ^~/api/ {
           proxy_pass http://x.x.x.x:8080/;
        }
        ...
    }}

配置的属性参数:
proxy_pass: 要代理到的url
proxy_http_version: 代理时使用的 http版本
proxy_set_header Host: http请求的主机域名
proxy_set_header X-Real-IP: 给代理设置原http请求的ip,填写$remote_addr 即可
proxy_set_header X-Forwarded-For: 反向代理之后转发之前的IP地址
proxy_set_header Upgrade: 把代理时http请求头的Upgrade 设置为原来http请求的请求头,wss协议的请求头为websocket
proxy_set_header Connection: 因为代理的wss协议,所以http请求头的Connection设置为Upgrade
在此特别注意: https是加密请求,需要SSL加密,所以nginx支持https,需要配置SSL
解决方案的链接:nginx配置ssl证书实现https