系列文章: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>
服务器的实现
- 安装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文件。在服务器端控制台输入文字,浏览器就可以实时收到了。