websocket 的实现原理 使用node搭建长连接

简介: HTTP/1.1为了尽可能的提高HTTP性能,1.1规定所有连接必须是持久的,已经不需要在头部加上Connection:Keep-alive了。

序言


浏览器实现长连接


在过去到现在,浏览器需要实现长连接有以下几种方式:


第一种是基于http协议来可以有两种方式:


1.在客户端使用轮询的方式,缺点是:增加不必要的http请求,浪费服务器的带宽


2.HTTP/1.0通过Connection:Keep-alive来实现长连接。

HTTP/1.1为了尽可能的提高HTTP性能,1.1规定所有连接必须是持久的,已经不需要在头部加上Connection:Keep-alive了。


想要短连接可以,Connection:close 但一般没人会主动去使用。仔细观察一下你的请求,有时候你可以在响应头上看到这个参数。


3.使用websocket连接方式,客户端和服务器可以保持持久的连接,也可以一方进行中断,然后就可以断开连接


原理


websockt 和 http 都是基于socket来的,而socket 是基于 tcp/ip 协议来的。


socket


1.客户端连接服务器(TCP / IP),三次握手,建立了连接通道


2.客户端和服务器通过socket接口发送消息和接收消息,任何一端在任何时候,都可以向另一端发送任何消息


3.有一端断开了,通道销毁


http


1.客户端连接服务器(TCP / IP),三次握手,建立了连接通道


2.客户端发送一个http格式的消息(消息头 消息体),服务器响应http格式的消息(消息头 消息体)


3.客户端或服务器断开,通道销毁


实时性的问题:


1.轮询


2.长连接


websocket


专门用于解决实时传输的问题


1.客户端连接服务器(TCP / IP),三次握手,建立了连接通道


2.客户端发送一个http格式的消息(特殊格式),服务器也响应一个http格式的消息(特殊格式),称之为http握手


3.双发自由通信,通信格式按照websocket的要求进行


4.客户端或服务器断开,通道销毁


在websocket的http握手阶段,服务器响应头中需要包含如下内容:


Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: [key]


其中,Sec-WebSocket-Accept的值来自于以下算法:


base64(sha1(Sec-WebSocket-Key) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 


在node中可以使用以下代码获得:


const crypto = require("crypto");
const hash = crypto.createHash("sha1");
hash.update(requestKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
const key = hash.digest("base64");


其中,requestKey来自于请求头中的Sec-WebSocket-Key


代码实现


使用原生的方式实现


node 服务端实现


const net = require("net");
const server = net.createServer((socket) => {
  console.log("收到客户端的连接");
  socket.once("data", (chunk) => {
    const httpContent = chunk.toString("utf-8");
    let parts = httpContent.split("\r\n");
    parts.shift();
    parts = parts
      .filter((s) => s)
      .map((s) => {
        const i = s.indexOf(":");
        return [s.substr(0, i), s.substr(i + 1).trim()];
      });
    const headers = Object.fromEntries(parts);
    const crypto = require("crypto");
    const hash = crypto.createHash("sha1");
    hash.update(
      headers["Sec-WebSocket-Key"] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    );
    const key = hash.digest("base64");
    // 响应
    socket.write(`HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ${key}
`);
    socket.on("data", (chunk) => {
      console.log(chunk,'来自浏览器的数据');
    });
  });
});
server.listen(5008);


客户端浏览器实现方式


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button>发送数据到服务器</button>
    <script>
      // 客户端(浏览器)websocket
      const ws = new WebSocket("ws://localhost:5008"); // 创建一个websocket,同时,发送连接到服务器
      ws.onopen = function () {
        // http握手完成
        console.log("连接已建立");
      };
      ws.onmessage = function (e) {
        console.log("来自服务器的数据", e.data);
      };
      ws.onclose = function () {
        console.log("通道关闭");
      };
      document.querySelector("button").onclick = function () {
        ws.send("123");
      };
      //   ws.close(); //客户端主动断开连接
    </script>
  </body>
</html>


使用插件来进行实现


npm install --save socket.io 这个库既可以用户客户端,也可以用于服务端 传送门


node端


const express = require("express");
const socketIO = require("socket.io");
const http = require("http");
const path = require("path");
// express
const app = express();
const server = http.createServer(app);
app.use(express.static(path.resolve(__dirname, "public")));
// websocket
const io = socketIO(server);
io.on("connection", (socket) => {
  // 当有一个新的客户端连接到服务器成功之后,触发的事件
  console.log("新的客户端连接进来了");
  socket.on("msg", (chunk) => {
    // 监听客户端的msg消息
    console.log(chunk.toString("utf-8"));
  });
  const timer = setInterval(function () {
    //每隔两秒钟,发送一个消息给客户端,消息为test
    socket.emit("test", "test message from server");
  }, 2000);
// 断开连接
  socket.on("disconnect", () => {
    clearInterval(timer);
    console.log("closed");
  });
});
// 监听端口
server.listen(5008, () => {
  console.log("server listening on 5008");
});


客户端


<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button>发送数据到服务器</button>
    // 可以使用sdn,或者是直接安装
    <script src="https://cdn.bootcdn.net/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script>
    // 创建一个连接
      const socket = io.connect();
      document.querySelector("button").onclick = function () {
      // 发送消息
        socket.emit("msg", "msg from client");
      };
// 获取服务端test的消息
      socket.on("test", (chunk) => {
        console.log(chunk);
      });
// 断开连接
      socket.on("disconnect", () => {
        console.log("closed");
      });
    </script>
  </body>
</html>


相关文章
|
Web App开发 前端开发 Java
SpringBoot默认200个线程对于Websocket长连接够用吗?(一)
上篇推文从源码剖析SpringBoot中Tomcat的默认最大连接数中我们知道,SpringBoot的内嵌Tomcat默认的最大连接数为200。那么,这个默认值对于项目中引入了WebSocket使用长连接后,是否足够用呢?今天强哥就带大家一起从源码的角度来分析一下。
SpringBoot默认200个线程对于Websocket长连接够用吗?(一)
|
6月前
|
存储 JavaScript 前端开发
webSocket+Node+Js实现在线聊天(包含所有代码)
文章介绍了如何使用WebSocket、Node.js和JavaScript实现在线聊天功能,包括完整的前端和后端代码示例。
339 0
|
10月前
|
监控 JavaScript API
局域网监控软件的实时通知系统:利用Node.js和WebSocket实现即时消息推送
本文介绍了如何使用Node.js和WebSocket构建局域网监控软件的实时通知系统。实时通知对于网络安全和家庭监控至关重要,能即时发送监控数据变化的通知,提高响应速度。通过Node.js创建WebSocket服务器,当数据变化时,监控软件发送消息至服务器,服务器随即推送给客户端。此外,还展示了如何利用Node.js编写API,自动将监控数据提交到网站,便于用户查看历史记录,从而提升监控体验。
232 3
|
6月前
|
JavaScript 前端开发 开发工具
五子棋小游戏(JS+Node+Websocket)可分房间对战
本文介绍了通过JS、Node和WebSocket实现的五子棋游戏,支持多人在线对战和观战功能。
160 1
五子棋小游戏(JS+Node+Websocket)可分房间对战
|
5月前
|
JavaScript 前端开发 API
Node.js 中的 WebSocket 底层实现
Node.js 中的 WebSocket 底层实现
113 0
|
JavaScript
node.js: ws服务端和WebSocket客户端交互示例
node.js: ws服务端和WebSocket客户端交互示例
699 0
|
关系型数据库 MySQL PHP
php使用webSocket实现Echarts长连接自动刷新的解决方案(3):获取读取数据库数据队列进行实时刷新
php使用webSocket实现Echarts长连接自动刷新的解决方案(3):获取读取数据库数据队列进行实时刷新
195 0
|
JSON 关系型数据库 MySQL
php使用webSocket实现Echarts长连接自动刷新的解决方案(2):后端服务端代码返回json数据
php使用webSocket实现Echarts长连接自动刷新的解决方案(2):后端服务端代码返回json数据
207 0
|
JSON 前端开发 JavaScript
php使用webSocket实现Echarts长连接自动刷新的解决方案(1):前端获取后端JSON数据
php使用webSocket实现Echarts长连接自动刷新的解决方案(1):前端获取后端JSON数据
207 0

热门文章

最新文章