一、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 原理的更多相关文章

  1. 教你成为全栈工程师(Full Stack Developer) 四十五-一文读懂hadoop、hbase、hive、spark分布式系统架构

    转载自http://www.shareditor.com/blogshow?blogId=96 机器学习.数据挖掘等各种大数据处理都离不开各种开源分布式系统,hadoop用于分布式存储和map-red ...

  2. Salesforce学习之路-developer篇(五)一文读懂Aura原理及实战案例分析

    1. 什么是Lightning Component框架? Lightning Component框架是一个UI框架,用于为移动和台式设备开发Web应用程序.这是一个单页面Web应用框架,用于为Ligh ...

  3. 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?

    本文引用了“蔷薇Nina”的“Nginx 相关介绍(Nginx是什么?能干嘛?)”一文部分内容,感谢作者的无私分享. 1.引言   Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡 ...

  4. 一文读懂Java动态代理

    作者 :潘潘 日期 :2020-11-22 事实上,对于很多Java编程人员来说,可能只需要达到从入门到上手的编程水准,就能很好的完成大部分研发工作.除非自己强主动获取,或者工作倒逼你学习,否则我们好 ...

  5. 一文读懂HTTP/2及HTTP/3特性

    摘要: 学习 HTTP/2 与 HTTP/3. 前言 HTTP/2 相比于 HTTP/1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何 ...

  6. 一文读懂高性能网络编程中的I/O模型

    1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...

  7. 从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.引言 HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HT ...

  8. [转帖]从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路

    从HTTP/0.9到HTTP/2:一文读懂HTTP协议的历史演变和设计思路   http://www.52im.net/thread-1709-1-2.html     本文原作者阮一峰,作者博客:r ...

  9. 大数据篇:一文读懂@数据仓库(PPT文字版)

    大数据篇:一文读懂@数据仓库 1 网络词汇总结 1.1 数据中台 数据中台是聚合和治理跨域数据,将数据抽象封装成服务,提供给前台以业务价值的逻辑概念. 数据中台是一套可持续"让企业的数据用起 ...

随机推荐

  1. java 的 IO简单理解

    首先要先理解什么是 stream ? stream代表的是任何有能力产出数据的数据源,或是任何有能力接收数据的接收源. 一.不同导向的 stream 1)以字节为单位从 stream 中读取或往 st ...

  2. Ionic命令笔记

    Ionic命令:ionic serve 开启服务调试ionic cordova prepare android 生成android原生项目 ionic cordova run browser 打包成混 ...

  3. P4827「国家集训队」 Crash 的文明世界

    「国家集训队」 Crash 的文明世界 提供一种不需要脑子的方法. 其实是看洛谷讨论版看出来的( (但是全网也就这一篇这个方法的题解了) 首先这是一个关于树上路径的问题,我们可以无脑上点分治. 考虑当 ...

  4. python pandas 读excel类

    import pandas as pd '''特例说明 file1="a20201220.xlsx" sheetname='Sheet1' df=pd.read_excel(fil ...

  5. CSS 样式清单整理

    1.文字超出部分显示省略号 单行文本的溢出显示省略号(一定要有宽度) p{ width:200rpx; overflow: hidden; text-overflow:ellipsis; white- ...

  6. 谷粒商城--分布式基础篇P28~P101(完结)

    谷粒商城--分布式基础篇P28~P101(完结) 前面1-27节主要是环境配置特别难,后面的28~101节主要是前端编写的代码较多以及后台的CRUD操作比较多.因为内容很多,所以我是根据自己想学的点进 ...

  7. Java 将Word转为XML,XML转为Word的方法

    本文介绍将Word和XML文档进行双向互转的方法.转换时,Word支持.docx/.doc等格式. 代码环境如下: Word测试文档:.docx或.doc 编译环境:IntelliJ IDEA JDK ...

  8. 什么是EL表达式,以及作用

    1.概念 EL(Expression Language) 是为了使JSP写起来更加简单.减少java代码,便于开发和维护. 2.语法 格式都是以"${}"表示. 3.与运算符 EL ...

  9. pytest框架运用

    import pytest ''' 运行方式 1. pytest -s test01.py 把print信息打印出来运行用例 2. 通过main运行 前置后置方法 1. 函数级 setup teard ...

  10. Ubuntu21.04 / Linux Mint20.2 安装 TradingView分析软件

    TradingView 是我们经常使用的一个分析工具!在Linux 下安装桌面版,就可以爽快的用起来了,目前是Beta测试版,相信很快就会有正式版的 1.打开终端,执行下列命令,更新系统到最新 sud ...