一、WebSocket 概述

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。它实现了浏览器与服务器全双工通信。在这之前都是客户端主动请求服务端,一请求一应答,很多时候实现消息更新都是采用 ajax 轮询,有延迟。自从有了 WebSocket 双方都可以主动发给对端,实现真正的推送。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,浏览器和服务器之间就可以形成一条快速通道,两者之间就直接可以创建持久性的连接,并进行双向数据传输

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

下面借鉴一张图来给大家更清晰的认识:

WebSocket 与 http 通信
浏览器通过 js 向服务器发出建立 WebSocket 连接的请求,连接建立成功以后,客户端和服务器端就可以通过 TCP 连接直接进行双向数据的交换。

当你获取 WebSocket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

总结:当在后台服务器端设置了 WebSocket 地址,服务器开启之后会等待前端客户端去连接它。当一个客户端打开去连接这个 WebSocket 地址,同时会给服务器端传递某些识别参数。这样客户端与服务器端就会连接成功了,然后客户端就可以给服务器端发送消息,服务器端也可以响应消息给客户端了。


二、TCP/IP 协议简述

想看关于UDP的内容,请移步至 【亲自实践总结】微信小程序WebSocket通信之UDP通信demo

TCP/IP 实际上是一个协议族,其中每一个都提供不同的功能。
TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。

关于 TCP/IP 和 HTTP协议的关系,可以理解为:我们在传输数据时,可以只使用TCP/IP协议(传输层),但是如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,就必须使用应用层的协议(比如HTTP、FTP等)。WEB 通过使用HTTP协议作为应用层协议,来封装HTTP信息,然后使用TCP/IP 做为传输层协议将这些信息发到网络上。

  • TCP(传输控制协议)特点:

    • TCP提供一对一的、面向连接的、可靠的网络数据传递服务;
    • TCP 在连接中会进行大数据传输(数据大小不做限制);
    • 必须建立连接,效率会稍低;
    • 使用TCP的应用:Web浏览器、电子邮件、文件传输程序;
  • TCP工作原理:

在这里我就不过多赘述它的工作原理了,感兴趣的小伙伴可以去百度一下“ TCP的工作原理 ”,能够回答这个问题的太多了。

简单来说分为两部分:

  • TCP的连接是 三次握手的过程
  • TCP终止连接是 四次挥手 来断开双向连接

在这里就开始正式写关于 tcp 通信的 WebSocket 的 demo 了。
虽然网上有很多关于 tcp 通信的相关内容,但是可以真正让一个前端开发人员轻松上手的 demo 并没有,关于小程序的相关 demo 更是少的可怜。
好了,废话不多说,请认真往下看。

三、基于tcp通信的客户端代码(微信小程序)

因为是在写 demo ,所以我是直接在 app.js 中直接添加代码,这样做的好处就是只要已进入小程序,就会自动连接到 WebSocket,省去了其他多余的操作。只有明白其原理,后续的很多业务操作就会很简单了。

参考文档:微信小程序 · WebSocket

在小程序中使用 WebSocket 主要用到的API
创建连接:wx.connectSocket、wx.onSocketOpen
监听连接:wx.onSocketMessage、wx.onSocketError、wx.onSocketClose
关闭连接:wx.closeSocket

App({
  globalData: {
    socketStatus: 'closed',  // 标识是否开启socket
    socketMsgQueue: ['hello'] // 发送的数据,也可以是其他形式
  },
  onLaunch: function () {
    let that = this;
    if (that.globalData.socketStatus === 'closed') {
      that.openSocket();
    }
  },
  
  // 打开socket信道
  openSocket() {
	// 注意:通过 WebSocket 连接发送数据。需要先 wx.connectSocket,并在 wx.onSocketOpen 回调之后才能发送。
	// 创建一个 WebSocket 连接
    wx.connectSocket({
      url: 'wss://' + 'localhost' + ':9999', // 如果使用http协议,url的开头应该是'ws',这里是https协议
    })

    // 打开时的动作
    wx.onSocketOpen((res) => {
      console.log('WebSocket 已连接', res)
      this.globalData.socketStatus = 'connected'; // 标记打开
      for (let i = 0; i < this.globalData.socketMsgQueue.length; i++) {
        this.sendSocketMessage(this.globalData.socketMsgQueue[i])
      }
      this.globalData.socketMsgQueue = []
    })

    // 监听 WebSocket 接受到服务器的消息事件
    wx.onSocketMessage(msg => {
      //把JSONStr转为JSON
      if (typeof msg !== 'object') {
        msg = msg.replace(/\ufeff/g, "");
        var jj = JSON.parse(msg);
        msg = jj;
      }
      console.log("【websocket 监听到消息】内容如下:", msg);
    })

    // 断开时的动作
    wx.onSocketClose((res) => {
      console.log('WebSocket 已断开' , res)
      this.closeSocket()
      this.globalData.socketStatus = 'closed'
    })

    // 报错时的动作
    wx.onSocketError(error => {
      console.error('socket error:', error)
    })
  },

  // 关闭信道
  closeSocket() {
    // 注意这里有时序问题,
    // 如果 wx.connectSocket 还没回调 wx.onSocketOpen,而先调用 wx.closeSocket,那么就做不到关闭 WebSocket 的目的。
    // 必须在 WebSocket 打开期间调用 wx.closeSocket 才能关闭。
    if (this.globalData.socketStatus === 'connected') {
      // 关闭 WebSocket 连接
      wx.closeSocket({
        success: (res) => {
          console.log("关闭 WebSocket 连接" , res);
          this.globalData.socketStatus = 'closed'
        }
      })
    }
  },

  //发送消息函数
  sendSocketMessage(msg) {
    if (this.globalData.socketStatus === 'connected') {
      // 自定义的发给后台识别的参数 ,我这里发送的是数组里面的内容
      wx.sendSocketMessage({
        data: msg
      })
    } else {
      this.globalData.socketMsgQueue.push(msg)
    }
  },
})

至此,小程序端的 demo 代码就已经完成了。只有服务器端的代码运行起来,就可以进行收发消息了。


四、基于tcp通信的服务器端代码(node.js)

现在大部分都是将 nodejs 的 socket.io 作为 WebSocket 的解决方案,但是 socket.io 是对 websocket 的封装,它并不支持 H5 原始的 websocket 协议,巧的是小程序使用的 websocket 就是一种接近于H5 原始的 websocket 协议,所以现在看来,在这里是用不到 socket.io 了。
虽然碰到这种情况,但是也不需要我们自己去实现 websocket 服务端,因为 node 很强大呀,他有很多 websocket 库,比如 ws,在 express 框架下也可以轻松实现的。我们在这里就用 ws 来针对微信小程序来实现websocket。
由于使用的是 https,所以就要使用到 ssl,关于 ssl 的获取方式,大家可以看一下11种免费获取 SSL 证书的方式
获取到 ssl 证书之后,最后我们使用到的文件是key和crt文件。

上面的准备工作准备好之后就可以开始我们的 node 征程了。

websocket(SSL)服务端代码实现:wss_demo.js

let fs = require('fs');
let cfg = {
    ssl: true,
    port: 9999,
    ssl_key: '../server/2_xx.cn.key', // ssl中.key 文件的路径地址
    ssl_cert: '../server/1_xx.cn_bundle.crt'  // ssl中.crt 文件的路径地址
};
let httpServ = (cfg.ssl) ? require('https') : require('http');
let WebSocketServer = require('ws').Server;
let app = null;
let processRequest = function(req, res) {
    res.writeHead(200);
    res.end('WebSockets!\n');
};
 
if (cfg.ssl) {
    app = httpServ.createServer({
        key: fs.readFileSync(cfg.ssl_key),
        cert: fs.readFileSync(cfg.ssl_cert)
    }, processRequest).listen(cfg.port);
} else {
    app = httpServ.createServer(processRequest).listen(cfg.port);
}
let wss = new WebSocketServer({
    server: app
});
 
wss.on('connection', function(wsConnect) {
    wsConnect.on('message', function(message) {
        console.log("服务端接收到消息:" + message);
        wsConnect.send("服务端返回接收到的消息:" + message);
    });
});

服务端的代码基本完成,但是如果想要跑起来,还差一步:安装 ws 库

npm install ws

运行:

node wss_demo.js

至此,就可以进行前后端相互通信了。

nodejs 端代码参考链接:https://my.oschina.net/waterbear/blog/788100


一个简单的基于微信小程序的 websocket 客户端与服务器端 通信轻松实现。如果觉得对你有帮助,欢迎点赞转发,有任何问题欢迎评论区留言讨论。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐