HTML5(十二)——一文读懂 WebSocket 原理
一、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 建立连接,代码如下:
let ws= new WebSocket('ws://localhost:8888')
ws.onopen = function(){
console.log("连接")
}
2.2、握手阶段
客户端与服务器建立连接之后,客户端发送握手请求,随后服务器发送握手响应即完成握手阶段。
客户端握手请求如下:
'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的技术之一,有巨大应用前景。
HTML5(十二)——一文读懂 WebSocket 原理的更多相关文章
- 教你成为全栈工程师(Full Stack Developer) 四十五-一文读懂hadoop、hbase、hive、spark分布式系统架构
转载自http://www.shareditor.com/blogshow?blogId=96 机器学习.数据挖掘等各种大数据处理都离不开各种开源分布式系统,hadoop用于分布式存储和map-red ...
- Salesforce学习之路-developer篇(五)一文读懂Aura原理及实战案例分析
1. 什么是Lightning Component框架? Lightning Component框架是一个UI框架,用于为移动和台式设备开发Web应用程序.这是一个单页面Web应用框架,用于为Ligh ...
- 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?
本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言 Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...
- 一文读懂Java动态代理
作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...
- 一文读懂HTTP/2及HTTP/3特性
摘要: 学习 HTTP/2 与 HTTP/3. 前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何 ...
- 一文读懂高性能网络编程中的I/O模型
1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...
- 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路
本文原作者阮一峰,作者博客:ruanyifeng.com. 1.引言 HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HT ...
- [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路
从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路 http://www.52im.net/thread-1709-1-2.html 本文原作者阮一峰,作者博客:r ...
- 大数据篇:一文读懂@数据仓库(PPT文字版)
大数据篇:一文读懂@数据仓库 1 网络词汇总结 1.1 数据中台 数据中台是聚合和治理跨域数据,将数据抽象封装成服务,提供给前台以业务价值的逻辑概念. 数据中台是一套可持续"让企业的数据用起 ...
随机推荐
- idea中IDEA优化配置,提高启动和运行速度
IDEA优化配置,提高启动和运行速度 IDEA默认启动配置主要考虑低配置用户,参数不高,导致 启动慢,然后运行也不流畅,这里我们需要优化下启动和运行配置: 找到idea安装的bin目录: D:\ide ...
- 南京大学计算机基础 X64函数调用和链接器原理和可重定位的文件.o
一. 1.函数调用差别 X64的函数调用,和X86函数调用区别,在于参数的传递不一样了,X64的参数传递不在依靠栈来传,而是寄存器,不过还是具有局限性的 比如只能允许六个寄存器来传,分别是RDI,RS ...
- Linux基本操作 [转]
前言 只有光头才能变强 这个学期开了Linux的课程了,授课的老师也是比较负责任的一位.总的来说也算是比较系统地学习了一下Linux了~~~ 本文章主要是总结Linux的基础操作以及一些简单的概念~如 ...
- 微软商业智能系列(一)初识微软BI
随着移动互联网.云计算.物联网和大数据技术的广泛应用,社会已经迈入全新的数据时代.掌握数据资产,进行智能化决策,已成为企业胜出的关键.越来越多的企业开始重视数据战略布局,重新定义自己的核心竞争力,从数 ...
- C语言:常量写法
float a=7.5f; //7.5为浮点数 long b=100L; //100为长整数 int c=0123;// 0123为8进制数 int d=0x123;//0x123为16进制数
- C语言怎么实现可变参数
可变参数 可变参数是指函数的参数的数据类型和数量都是不固定的. printf函数的参数就是可变的.这个函数的原型是:int printf(const char *format, ...). 用一段代码 ...
- mybatis-7-缓存
1. 一级缓存: SqlSession 级别, 默认开启, 并且不能关闭 操作数据库是需要创建 SqlSession 对象, 在对象中有一个 HashMap 用于存储缓存数据, 不同的 SqlSess ...
- windows系统下 PHP怎么安装redis扩展
在windows系统下安装redis就不赘述了,基本上就是下一步,下一步. 然后通过通过命令行启动服务. 我是在xamp 3.2.2的集成环境下进行本地redis扩展安装配置的,php的版本是5.6. ...
- 动态 WebApi 引擎使用教程(3行代码完成动态 WebApi 构建)
目录 什么是 WebApiEngine? 开源地址 使用方法 使用 [ApiBind] 标签让任何方法变成 WebApi 对 API 进行分类 自定义 API 名称 复制特性 为整个类配置 WebAp ...
- 【Uva11400 Lighting System Design】动态规划
分析 先按照电压从小到大排序,做一下前缀和s[i]求i之前的电灯泡的数量. 状态:$ F_i\(表示到\) i$个灯泡的最小开销. 状态转移方程:$ F_i=F_j+(s[i]-s[j])\times ...