WebSocket简介(二)——实战篇

1,221 阅读2分钟

系列文章:WebSocket简介(一)——理论篇

使用到的技术&工具

此次实战使用WebSocket协议进行了客户端和服务器之间的全双工通信。 其中,客户端使用的是WebSocket提供的原生API,而服务器使用的是WebSocket协议在node端的实现——WebSocket-Node

还有一个使用比较多的库——socket.io,但是有一点要注意,socket.io并不完全兼容WebSocket。这是因为socket.io在底层实现方式上并没有完全采用WebSocket协议,而是在每个数据包中添加了一些元数据。具体官方给出了解释:

Socket.IO is NOT a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the packet id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server either. Please see the protocol specification here.

所以socket.io和WebSocket不能混用。也就是说,使用socket.io创建的服务器不能用于使用原生WebSocket的客户端;同理,使用了socket.io的客户端也不能直接与一个WebSocket服务器通信。

实现效果

最终实现的效果是服务端控制台输入文字,实时发送到前端显示。

客户端的实现

为了加深对WebSocket的理解,客户端没有使用第三方封装的库,而是直接使用了原生的WebSocket API。

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Websocket</title>
</head>
<body>
<h2>Console</h2>
<div id="dashboard"></div>
<script>
  const dashboard = document.querySelector('#dashboard');
  const appendData = (data) => {
    const p = document.createElement('p');
    p.textContent = data.toString();
    dashboard.appendChild(p);
  };
  
  // 1. 连接WebSocket服务器,由于在本地的8080端口,所以地址是localhost:8080,协议是ws;第二个参数是服务器和前端约定的协议
  let ws = new WebSocket('ws://localhost:8080', 'echo-protocol');
  
  // 2. 监听open事件,一旦连接打开,就向服务器发送一条打招呼的消息
  ws.addEventListener('open', (e) => {
    appendData('websocket open');
    console.log('open', e);
    ws.send('send message: hello from frontend!');
  });
  
  // 3. 监听服务器发来的消息,并输出到页面的元素中
  ws.addEventListener('message', (e) => {
    appendData('received message: ' + e.data);
    console.log('message', e);
  });
  
  // 4. 监听连接关闭事件
  ws.addEventListener('close', (e) => {
    appendData('websocket close');
    console.log('close: ', e);
  });
  
  // 5. 监听错误事件
  ws.addEventListener('error', (e) => {
    appendData('error');
    console.log('error: ', e);
  });
</script>
</body>
</html>

服务器的实现

  1. 安装WebSocket-node: yarn add websocket
// server.js
// 2. 引入模块
let websockerServer = require('websocket').server;
let http = require('http');

let log = (message) => {
  console.log((new Date()) + ': ' + message);
};

// 3. 创建HTTP服务器,因为WebSocket握手阶段使用的是HTTP,所以需要一个HTTP服务器
let server = http.createServer((request, response) => {
  log('received request for' + request.url);
  response.writeHead(404);
  response.end();
});

// 4. 监听8080端口
server.listen(8080, () => {
  log('server is listening on port 8080');
});

// 5. 创建WebSocket服务器,httpServer选项填入已创建的HTTP服务器
let wsServer = new websockerServer({
  httpServer: server,
  autoAcceptConnections: false,
});

// 6. 监听请求并处理
wsServer.on('request', (request) => {
  let connection = request.accept('echo-protocol', request.origin);
  log('connection accepted');

  // 封装发送消息的方法
  let send = message => {
    // 发送utf-8编码格式的消息
    connection.sendUTF(message, () => {
      log('send message: ' + message);
    });
  };
  
  // 向客户端发送一个打招呼的消息
  send('hello from websocketServer');

  // 这一步是为了在控制台输入数据,监听到输入的数据后,发送给客户端
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', chunk => {
    send(chunk);
  });

  // 监听客户端发来的消息,这里接收utf-8编码格式的消息
  connection.on('message', (message) => {
    if (message.type === 'utf8') {
      log('received message: ' + message.utf8Data);
    }
  });

  // 监听关闭事件
  connection.on('close', (reasonCode, description) => {
    log('Peer ' + connection.remoteAddress + ' disconnected.');
  });
});

一切准备就绪后,执行node server.js,浏览器中打开html文件。在服务器端控制台输入文字,浏览器就可以实时收到了。

Reference

  1. www.ruanyifeng.com/blog/2017/0…