研究一下WebSocket
JavaScript
2021-05-11
395
0

为什么需要 WebSocket?

HTTP 协议做不到服务器主动向客户端推送信息,只能由客户端向服务端主动发起请求来拉取信息。这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询",即每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。但是,轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法,WebSocket因此而被发明。

Websocket的特性

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。
  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
  7. 浏览器兼容性从IE10开始支持。

服务端实现

WebSocket 服务器的实现,可以查看维基百科的列表

常用的 Node 实现有以下三种。

Socket.IO 实现

Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯、通知与消息推送,实时分析等场景中有较为广泛的应用。

安装命令:

npm install --save express socket.io

我们按照官网教程,简单实现一个聊天室。

服务端index.js逻辑代码

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/index.html');
});

io.on('connection', (socket) => {
    console.log('a user connected');
    // 当有chat message来的时候处理
    socket.on('chat message', (msg) => {
        console.log('message: ' + msg);

        // 接下来需要把这个消息广播给所有连接着的用户
        io.emit('chat message', msg);
    });
});

server.listen(3000, () => {
    console.log('listening on *:3000');
});

客户端index.html代码

<!DOCTYPE html>
<html>

<head>
    <title>Socket.IO chat</title>
    <style>
        body {
            margin: 0;
            padding-bottom: 3rem;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        }

        #form {
            background: rgba(0, 0, 0, 0.15);
            padding: 0.25rem;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            display: flex;
            height: 3rem;
            box-sizing: border-box;
            backdrop-filter: blur(10px);
        }

        #input {
            border: none;
            padding: 0 1rem;
            flex-grow: 1;
            border-radius: 2rem;
            margin: 0.25rem;
        }

        #input:focus {
            outline: none;
        }

        #form>button {
            background: #333;
            border: none;
            padding: 0 1rem;
            margin: 0.25rem;
            border-radius: 3px;
            outline: none;
            color: #fff;
        }

        #messages {
            list-style-type: none;
            margin: 0;
            padding: 0;
        }

        #messages>li {
            padding: 0.5rem 1rem;
        }

        #messages>li:nth-child(odd) {
            background: #efefef;
        }
    </style>
</head>

<body>
    <ul id="messages"></ul>
    <form id="form" action="">
        <input id="input" autocomplete="off" /><button>Send</button>
    </form>


    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io();

        var form = document.getElementById('form');
        var input = document.getElementById('input');

        // 当用户点击提交按钮的时候
        form.addEventListener('submit', function(e) {
            e.preventDefault(); // 阻止页面默认刷新
            if (input.value) {
                // 发送内容
                socket.emit('chat message', input.value);
                input.value = '';
            }
        });

        // 当收到了广播消息时
        socket.on('chat message', (msg) => {
            var item = document.createElement('li');
            item.textContent = msg;
            messages.appendChild(item);
            window.scrollTo(0, document.body.scrollHeight);
        });
    </script>

</body>

</html>

WS实现

socket.io是一套完整的前后端socket封装逻辑,如果前端是小程序,那么socket.io就无能为力了。这里我们可以试一试WS这个库。

安装 WS库

npm install --save ws

服务端

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

// ws没有广播函数,所以需要存储所有人的ws对象,然后遍历发送
let ws_arr = [];

wss.on('connection', function connection(ws) {
    console.log("Some one connect...");
    //新来一个连接,存入连接数组中
    ws_arr.push(ws);

    // 来消息了
    ws.on('message', (message) => {
        console.log('received: %s', message);

        // 遍历所有人,然后广播
        ws_arr.forEach(ws => {
            ws.send(message);
        })
    });
});

微信小程序端

// pages/websocket/websocket.js
Page({

  data: {

  },

  onLoad: function (options) {
    // 先连接接口
    wx.connectSocket({
      url: 'ws://localhost:8080',
    })
    // 当来消息了,用提示框弹出消息
    wx.onSocketMessage((result) => {
      wx.showToast({
        title: result.data,
      })
    })
  },
  // 发送消息函数
  sendMsg() {
    wx.sendSocketMessage({
      data: "hahaha",
    })
  }
})

Reference

Websocket