WebSocket 详解

一、前言

WebSocket 是 HTML5 新增的一种全双工通信协议,客户端和服务器基于 TCP 握手连接成功后,两者之间就可以建立持久性的连接,实现双向数据传输。

二、WebSocket 和 HTTP

我们知道 HTTP 协议是一种单向的网络协议,在建立连接后,它允许客户端向服务器发送请求资源后,服务器才会返回相应的数据,而服务器不能主动推送数据给客户端。

当时为什么这样设计呢?假设一些不良广告商主动将一些信息强行推送给客户端,这不得不说是一个灾难。

所以现在我们要做股票的实时行情,或者获取火车票的剩余票数等,就需要客户端和服务器之间反复地进行 HTTP 通讯,客户端不断地发送 GET 请求,去获取当前的实时数据。

下面介绍几种常见的方式。

2.1 (短)轮询(Polling)

短轮询模式下,客户端每隔一段时间向服务器发送 HTTP 请求。

服务器收到请求后,将最新的数据发回给客户端。

这种情况下的弊端是非常明显的:某个时间段服务器没有更新内容,但是客户端每隔一段时间发送请求来询问,而这段时间内的请求是无效的。

这就导致了网络带宽的浪费。

2.2 长轮询

长轮询模式下,客户端向服务器发出请求,服务器并不一定会立即回应客户端,而是查看数据是否有更新。

如果数据更新了的话,那就立即发送数据给客户端;如果没有更新,那就保持这个请求,等待有新的数据到来,才将数据返回给客户端。

如果服务器长时间没有更新,那么一段时间后,请求变会超时,客户端收到消息后,会立即发送一个新的请求给服务器。

当然这种方式也有弊端:当服务器向客户端发送数据后,必须等待下一次请求才能将新的数据发出,这样客户端接收新数据就有一个最短时间间隔。

如果服务器更新频率较快,那么就会出现问题。

2.3 WebSocket

基于前面的情况,为了彻底解决服务器主动向客户端发送数据的问题。

W3C 在 HTML5 中提供了一种让客户端与服务器之间进行全双工通讯的网络技术 WebSocket。

WebSocket 基于 TCP 协议,是一种全新的、独立的协议,与 HTTP 协议兼容却不会融入 HTTP 协议,仅仅作为 HTML5 的一部分。

2.4 两者比对

基于上面,小伙伴们大概了解了 WebSocket 的缘由了,这里再总结对比一下 HTTP 和 WebSocket。

  • 相同点
  1. 都需要建立 TCP 连接
  2. 都属于七层协议中的应用层协议
  • 不同点
  1. HTTP 是单向数据流,客户端向服务器发送请求,服务器响应并返回数据;WebSocket 连接后可以实现客户端和服务器双向数据传递,除非某一端断开连接。
  2. HTTP 的 url 使用 http// 或者 https// 开头,而 WebSocket 的 url 使用 ws// 开头

三、Socket.io

我们先来看 WebSocket 的一个使用方式:

const ws = new WebSocket("ws//:xxx.xx", [protocol])

ws.onopen = () => {
  ws.send('hello')
  console.log('send')
}

ws.onmessage = (ev) =>{
  console.log(ev.data)
  ws.close()
}

ws.onclose = (ev) =>{
  console.log('close')
}

ws.onerror = (ev) =>{
  console.log('error')
}

WebSocket 实例化后,前端可以通过上面介绍的方法进行对应的操作,看起来还是蛮简单的。

但是,如果想完全搭建一个 WebSocket 服务端比较麻烦,又浪费时间。

所以:Socket.io 基于 WebSocket,加上轮询机制以及其他的实时通讯方面的内容,实现的一个库,它在服务端实现了实时机制的响应代码。

也就是说,WebSocket 仅仅是 Socket.io 实现通讯的一个子集。

因此,WebSocket 客户端连接不上 Socket.io 服务端,Socket.io 客户端也连不上 WebSocket 服务端。

下面我们讲解下如何实现一个简单的聊天。

3.1 服务端代码

package.json

{
  "devDependencies": {
    "express": "^4.15.2",
    "socket.io": "^2.3.0"
  }
}

index.js


let express = require('express');
let app = express();
let server = require('http').createServer(app);
let io = require('socket.io')(server);
let path = require('path');

app.use('/', (req, res, next) => {
  res.status(200).sendFile(path.resolve(__dirname, 'index.html'));
});

// 开启 socket.io
io.on('connection', (client) => {

  // 如果有新客户端进来,显示 ID
  console.log(`客户端 ID:${client.id}`);

  // 监听客户端的输入信息
  client.on('channel', (data) => {
    console.log(`客户端 ${client.id} 发送信息 ${data}`);
    io.emit('broadcast', data);
  });

  // 判断客户端是否关闭
  client.on('disconnect', () => {
    console.log(`客户端关闭:${client.id}`);
  });
});

server.listen(3000, () => {
  console.log('服务监听 3000 端口');
});

如上,我们直接通过 npm i 安装依赖包后,直接通过 node index.js 可以开启服务。

当然,如果小伙伴们想手动装包,执行下面命令即可:

npm i express socket.io express -D

3.2 客户端代码


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Socket.io</title>
  <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.slim.js"></script>
</head>

<body>

  <input type="text" id="input">
  <button id="btn">send</button>
  <div id="content-wrap"></div>

  <script>
    window.onload = function () {
      let inputValue = null;

      // 连接 socket.io
      let socket = io('http://localhost:3000');
      // 将创建的信息以添加 p 标签的形式展示成列表
      socket.on('broadcast', data => {
        let content = document.createElement('p');
        content.innerHTML = data;
        document.querySelector('#content-wrap').appendChild(content);
      })

      // 设置输入框的内容
      let inputChangeHandle = (ev) => {
        inputValue = ev.target.value;
      }
      // 获取输入框并监听输入
      let inputDom = document.querySelector("#input");
      inputDom.addEventListener('input', inputChangeHandle, false);

      // 当用户点击发送信息的时候,进行数据交互
      let sendHandle = () => {
        socket.emit('channel', inputValue);
      }
      let btnDom = document.querySelector("#btn");
      btnDom.addEventListener('click', sendHandle, false);

      // 打页面卸载的时候,通知服务器关闭
      window.onunload = () => {
        btnDom.removeEventListener('click', sendHandle, false);
        inputDom.removeEventListener('input', inputChangeHandle, false);
      }
    };
  </script>
</body>

</html>

3.3 小结

Socket.io 不仅支持 WebSocket,还支持许多种轮询机制以及其他实时通信方式,并封装了通用的接口。

这些方式包含 Adobe Flash Socket、Ajax 长轮询、Ajax multipart streaming、持久 Iframe、JSONP 轮询等。

换句话说,当 Socket.io 检测到当前环境不支持 WebSocket 时,能够自动地选择最佳的方式来实现网络的实时通信。

这样,我们就对 WebSocket 有一定的了解,面试的时候就不慌了。


  转载请注明: 森海北屿 ღ WebSocket 详解

 上一篇
网络安全 网络安全
一、前言浏览器安全可以分为三大块: Web 页面安全 浏览器网络安全 浏览器系统安全 Web 世界是开放的,任何资源都可以接入其中,我们的网站可以加载并执行别人网站的脚本文件、图片、音频、视频等资源,甚至可以下载其他站点的可执行文件。
2021-01-07
下一篇 
DNS解析 DNS解析
一、用户在浏览器的地址栏中敲入了网站的网址 ,会发生哪些事情呢? 用户在浏览器的地址栏中敲入了网站的网址 浏览器查找域名的IP地址 找到ip地址后,浏览器给web服务器发送一个HTTP请求 服务器“处理”请求 服务器发回一个HTML响应 浏
2021-01-03
  目录