百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

HTML5(十二)——一文读懂 WebSocket 原理

suiw9 2024-11-27 20:40 23 浏览 0 评论

一、WebSocket 由来

WebSocket 是一个持久化的协议,通过第一次 HTTP Request 建立连接之后,再把通信协议升级成 websocket,保持连接状态,后续的数据交换不需要再重复请求。websocket 可以看成一种类似 TCP/IP 的 socke t技术,在 web 应用中实现、并获得同 TCP/IP 通信一样的双向通信功能,因此客户端既和服务器可以发送消息也可以接收消息,同时还支持多路复用的功能,由于它借用了 HTTP 协议的一些概念,所以被称为 WebSocket。

webSocket API定义了web应用和服务器进行通信的公共接口,具体的构造函数创建对象、对象的属性、方法、事件及它的意义,在上一篇《HTML5(十一)——WebSocket 基础教程》文章中已详细介绍。

二、WebSocket 通信过程

WebSocket 协议可分为两部分:握手阶段和数据通信阶段。

WebSocket 为应用层协议,定义在 TCP/IP 协议栈之上,连接服务器的 url 是以 ws 或 wss 开头的。ws 开头的默认TCP端口为80,wss 开头的默认端口为443。

ws(websocket)是不安全的,容易被窃听,只要别人知道你的ip和端口号,任何人都可以去连接通讯。

wss(web socket secure)是websocket的加密版本。

2.1、建立连接

客户端去与服务器建立 TCP 连接,客户端生成 websocket 对象,然后使用 API 建立连接,代码如下:

Bash
let ws= new WebSocket('ws://localhost:8888')
ws.onopen = function(){
 console.log("连接")
}

2.2、握手阶段

客户端与服务器建立连接之后,客户端发送握手请求,随后服务器发送握手响应即完成握手阶段。

客户端握手请求如下:

Bash
'GET / HTTP/1.1',
 'Host: localhost:8888',
 'Connection: Upgrade',
 'Pragma: no-cache',
 'Cache-Control: no-cache',
 'Upgrade: websocket',
 'Origin: file://',
 'Sec-WebSocket-Version: 13',
 'Accept-Encoding: gzip, deflate, br',
 'Accept-Language: zh-CN,zh;q=0.9',
 'Sec-WebSocket-Key: In1aAp/ya9Lkv+tsUtXLXQ==',
 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits',

服务器握手响应如下:

Status Code: 101 Switching Protocols
Connection: Upgrade
sec-websocket-Accept: HBMDBbZMiS59r3aAITpGtJ64Mfc=
Upgrade: websocket

2.3、数据通讯

WebSocket 握手连接成功之后。可以使用 send 进行发送数据,onmessage 接收数据,如下发送“你好”:

let ws= new WebSocket('ws://localhost:8888')
ws.onopen = function(){
 console.log("连接成功")
 ws.send("你好")
}
ws.onmessage = function(res){
 console.log('接收到的消息',res)
}

服务器打印接收到的数据,如:<Buffer 81 86 af 87 53 b4 4b 3a f3 51 0a 3a>。

websocket 在发送数据时,被组织为一串数据帧,然后进行发送。传送的帧包含两部分:数据帧和控制帧。数据帧可以携带文本数据或者二进制数据,控制帧包含关闭帧和 Ping/Pong 帧。


  • FIN :1bit ,表示是消息的最后一帧,如果消息只有一帧那么第一帧也就是最后一帧。
  • RSV1,RSV2,RSV3:每个1bit,必须是0,除非扩展定义为非零。如果接受到的是非零值但是扩展没有定义,则需要关闭连接。
  • Opcode:4bit,解释Payload数据,规定有以下不同的状态,如果是未知的,接收方必须马上关闭连接。状态如下:0x0(附加数据帧) 0x1(文本数据帧) 0x2(二进制数据帧) 0x3-7(保留为之后非控制帧使用) 0xB-F(保留为后面的控制帧使用) 0x8(关闭连接帧) 0x9(ping) 0xA(pong)
  • Mask:1bit,掩码,定义payload数据是否进行了掩码处理,如果是1表示进行了掩码处理。Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
  • Payload length:7位,7 + 16位,7+64位,payload数据的长度,如果是0-125,就是真实的payload长度,如果是126,那么接着后面的2个字节对应的16位无符号整数就是payload数据长度;如果是127,那么接着后面的8个字节对应的64位无符号整数就是payload数据的长度。
  • Masking-key:0到4字节,如果MASK位设为1则有4个字节的掩码解密密钥,否则就没有。
  • Payload data:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。

把接收到的buffer十六进制数据转成二进制数据,控制帧与上述各个类型帧进行对比解析其意义。

2.4、关闭连接

任何一端可以关闭连接。客户端关闭连接如下:

ws.close()

然后发送关闭帧给对方,通常会带有关闭连接的状态码,常见的状态码如下:

  • 1000 连接正常关闭
  • 1001 端点离线,例如服务器down,或者浏览器已经离开此页面
  • 1002 端点因为协议错误而中断连接
  • 1003 端点因为受到不能接受的数据类型而中断连接
  • 1004 保留
  • 1005 保留, 用于提示应用未收到连接关闭的状态码
  • 1006 端点异常关闭
  • 1007 端点收到的数据帧类型不一致而导致连接关闭
  • 1008 数据违例而关闭连接
  • 1009 收到的消息数据太大而关闭连接
  • 1010 客户端因为服务器未协商扩展而关闭
  • 1011 服务器因为遭遇异常而关闭连接
  • 1015 TLS握手失败关闭连接

三、websocket 实例

3.1、客户端创建websocket对象,并建立连接之后发送数据。其中 ws 地址根据后台服务端口对应。

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
</head>
<body>
 <script>
  let ws= new WebSocket('ws://localhost:8888')
  ws.onopen = function(){
   console.log("连接")
   ws.send("你好")
   }
   ws.onmessage = function(res){
    console.log('res',res)
  }
 </script>
</body>
</html>

3.2、使用 node.js 创建一个 websocket 服务,如创建一个serve.js文件,代码如下:

const http = require("http")
const net = require("net") //原生的websocket
const crypto = require('crypto') // 安全性校验
let serve = net.createServer(sock=>{
 //只握手一次
 sock.once('data',(data)=>{
  console.log("hand shake start") // 开始握手
  let str = data.toString();
  let lines = str.split('\r\n')
  //舍弃第一行和最后两行
  lines = lines.slice(1,lines.length-2)
  let headers = {}
  lines.forEach(line=>{
   let [key,val] = line.split(': ')
   headers[key.toLowerCase()] = val
  })
  if( headers['upgrade']!= 'websocket' ){
    console.log("其他协议")
    sock.end()
  }else if(headers['sec-websocket-version']!=13){
   console.log("版本不对")
   sock.end()
  }else{
   let key = headers['sec-websocket-key']
   let mask = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
   //sha1(key+mask) -> base64 =>client
   let hash = crypto.createHash('sha1')
   hash.update(key+mask)
   let key2 = hash.digest('base64')
   sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade:websocket\r\nConnection:Upgrade\r\nsec-websocket-Accept:${key2}\r\n\r\n` )
   console.log("hand shake end") // 握手结束
   //真正的数据
   sock.on('data',res=>{
   console.log("真正接收数据",res)
   //数据解析
   let FIN = res[0]&0x001;
   let opcode = data[0]&0x0F0;
   let msak = data[1]&0x001;
   let payload = data[1]&0x0FE;          
  })
  }
 })
 //断开
 sock.on('end',()=>{
  console.log("连接已断开")
 })
})
serve.listen("8888")

使用命令 node serve.js 或node serve 启动服务,服务启动成功之后可以使用localhost:8888访问服务。

启动服务之后,访问前边创建的html文件访问websocket服务。

四、websocket的优点

  • 第一次通过http建立连接之后,数据交互不用发送http请求,节省了带宽资源。
  • websocket连接是双向通信,服务器和客户端既可接受也可发送消息。
  • websocket多路复用,几个不同url可以复用一个websocket服务。
  • 是HTML5的技术之一,有巨大应用前景。

相关推荐

谁说 Vim 不好用?送你一个五彩斑斓的编辑器!

相信大家在使用各种各样强大的IDE写代码时都会注意到,代码中各种类型的关键字会用独特的颜色标记出来,然后形成一套语法高亮规则。这样不仅美观,而且方便代码的阅读。而在上古神器Vim中,我们通常看...

Python 基础教程 九之cron定时执行python脚本

前言在Linux或Unix系统中,你可以使用cron任务来定时执行Python脚本。cron是一个基于时间的作业调度器,允许你安排命令或脚本在系统上自动执行。...

RK3588主板Ubuntu 22.04固件刷机后需要进行哪些操作?

RK3588主板Ubuntu22.04固件刷机完成后,您需要进行以下操作:1、验证固件版本:确认您刷入的Ubuntu22.04固件版本是否正确,并且系统正常运行。...

Ubuntu16.04搭建VNC Server远程桌面服务器

1.前言本文主要讲解Ubuntu16.04怎么安装VNCServer远程桌面服务器。VNC全称为VirtualNetworkComputing(虚拟网络计算)是一种图形化桌面共享工具,允许...

开源&amp;Docker:何必nextcloud,新晋神级私人云盘,在线office编辑

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:可爱的小cherry前言大家好,这里是可爱的Cherry。...

Ubuntu系统,常用命令、IP配置等教程

Ubuntu系统常用命令与IP配置教程一、常用命令在Ubuntu系统中,掌握一些常用的命令对于日常操作非常重要,以下是常见的命令及其用法说明:1.ls—列出当前目录内容...

Ubuntu 24.10安装体验(ubuntu 20.04.1安装教程)

内核版本...

Zed编辑器:Rust之力,Linux之翼,VS Code劲敌新篇章

Zed是一款支持多人协作...

如何在Ubuntu系统中进行系统级的代理设置

Ubuntu系统级代理设置步骤详解在Ubuntu系统中进行系统级的代理设置是一种常见的网络配置方式,尤其适用于需要通过代理访问互联网的场景。通过配置代理,你可以让系统中的所有应用程序(如浏览器、终端、...

Linux Vim文本编辑器(linux用vi编辑文本)

在使用Linux操作系统的过程中,经常需要对文本文件进行操作,如新建、编辑等,常用的方法有以下几种:...

从零入门Linux(五)文本编辑器(linux好用的文本编辑器)

Linux系统提供了多种文本编辑器,每种编辑器都有其独特的功能和使用场景。以下按功能和复杂程度分类介绍:1.简单文本编辑器...

照片选择器,使用简单,几行代码就可以完成集成

照片选择器,使用简单,几行代码就可以完成集成来源:极客头条用于发表图片时候选择相册图片和拍照这样的需求,虽然网上也有很多类似的控件,写的挺不错的,但是深入使用就有些问题,还是自己写算了;网上的一些轮...

探索iOS 9适配(iphone适配)

CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面。如果您想投稿,或寻求《近匠》报道,请发送邮件至tang...

“旅行者”回望30载 一点淡蓝“焕新颜”

据美国国家航空航天局(NASA)网站12日报道,为纪念著名的“暗淡蓝点”(PaleBlueDot)照片问世30周年,NASA重新制作了这张照片,使其“焕新颜”,NASA喷气推进实验室于近日发布了新...

「底层技术原理体系」深入探索Java服务器性能监控Metrics框架

承接上文承接上文中的【深度挖掘Java性能调优】「底层技术原理体系」深入探索Java服务器性能监控Metrics框架的实现原理分析(Counter篇),我们知道和了解了对应的Counter计数器的作用...

取消回复欢迎 发表评论: