本系列第一篇《[WebSocket 基础与应用系列(一)—— 抓个 WebSocket 的包] 》,没看过的同学可以看看,看过的同学也可以回顾一把。
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
Socket.IO 在 Socket.IO server (Node.js) 和 Socket.IO client ( browser, Node.js, or another programming language ) 之间,基于 WebSocket ( 不支持 WebSocket 的情况下,退化成 HTTP long-polling ) 建立一条全双工实时通信通道.
Engine.IO 是一个 Socket.IO 的抽象实现,作为 Socket.IO 的服务器和浏览器之间交换的数据的传输层。它不会取代 Socket.IO,它只是抽象出固有的复杂性,支持多种浏览器,设备和网络的实时数据交换。Engine.IO 使用了 Websocket 和 HTTP long-polling 方式封装了一套 socket 协议。为了兼容不支持 Websocket 的低版本浏览器,使用长轮询 ( polling ) 替代 WebSocket。
Engine.IO 负责在服务器和客户端之间建立底层连接。包括以下功能:
现在主要有 2 种传输通道实现
HTTP long-polling transport (也简称 "polling") 由连续的 HTTP requests 组成:
基于 HTTP long-polling transport 的特性,连续的 emits 可能合并在一个 HTTP Request 中发送。
The WebSocket 传输通道 包含一条 WebSocket 连接,WebSocket 提供了服务端和客户端之间双向通信及低时延的通信通道。
基于传输通道特性,每个 emit 会以一个 WebSocket 数据帧发送,有时候会分为 2 个不同的数据帧发送。
Engine.IO 连接建立的时候, Server 端会发送一些消息到客户端:
{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000
}
默认的情况下,客户端先建立 HTTP long-polling 通信通道。
为什么呢?
WebSocket 无疑是最好的双向通道,但是由于公司的代理、个人的防火墙、杀毒软件等,它并不是在什么情况下都能成功建立。
从用户的角度来看,如果 WebSocket 连接建立失败,那么用户至少要等 10S 才能开始真正的数据传输,这无疑伤害了用户的体验。
总的来说,Engine.IO 首先关注可靠性和用户体验,其次才是服务器性能。
升级的时候,客户端会做如下动作:
可以在浏览器抓包看到如下网络连接:
当以下情况出现时,Engine.IO 的连接会判断为关闭。
服务端会以 pingInterval 的间隔发送 PING 数据包,客户端收到后在 pingTimeout 时间之内需要发送 PONG 数据包给服务端,如果服务端在 pingTimeout 时间内没有收到,那么就认为这条连接关闭了。相反,客户端如果在 pingInterval + pingTimeout 时间内没有收到 PING 数据包,客户端也判断连接关闭。
服务端触发断连事件的原因有:
Reason | Description |
---|---|
server namespace disconnect | The socket was forcefully disconnected with socket.disconnect |
client namespace disconnect | The client has manually disconnected the socket using socket.disconnect() |
server shutting down | The server is, well, shutting down |
ping timeout | The client did not send a PONG packet in the pingTimeout delay |
transport close | The connection was closed (example: the user has lost connection, or the network was changed from WiFi to 4G) |
transport error | The connection has encountered an error |
客户端触发断连事件的原因有:
Reason | Description |
---|---|
io server disconnect | The server has forcefully disconnected the socket with socket.disconnect() |
io client disconnect | The socket was manually disconnected using socket.disconnect() |
ping timeout | The server did not send a PING within the pingInterval + pingTimeout range |
transport close | The connection was closed (example: the user has lost connection, or the network was changed from WiFi to 4G) |
transport error | The connection has encountered an error (example: the server was killed during a HTTP long-polling cycle) |
传输通道通过 Engine.IO URL 进行连接建立
连接建立之后,服务端会发一个 JSON 格式的握手数据
sid:会话 id (string)
upgrades: 允许升级的传输通道 (Array of String)
pingTimeout: 服务端配置的 ping 超时时间,发送给客户端,客户端用来检测服务端是否还正常响应 (Number)
pingInterval: 服务端配置的心跳间隔,客户端用来检测服务端是否还正常响应 (Number)
客户端收到服务端定时的 ping packet 之后,需要回复客户端 pong packet
客户端和服务端之间可以传输 message packets
Polling transports 可以发送 close packet 来关闭 socket
GET /engine.io/?EIO=4&transport=polling&t=N8hyd6w
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
0{"sid":"N-YWtQT1K9uQsb15AAAD","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}
Details:
0 => "open" packet type
{"sid":... => the handshake data
Note: query 参数中的 t 是用来防止浏览器缓存请求.
服务端执行 socket.send ('hey') :
GET /engine.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
4hey
Details:
4 => "message" packet type
hey => the actual message
Note: query 中的 sid 是握手协议中 sid.
客户端执行:socket.send ('hello'); socket.send ('world');
POST /engine.io/?EIO=4&transport=polling&t=N8hzxke&sid=lv_VI97HAXpY6yYWAAAC
> Content-Type: text/plain; charset=UTF-8
4hello\x1e4world
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=UTF-8
ok
Details:
4 => "message" packet type
hello => the 1st message
\x1e => separator
4 => "message" message type
world => the 2nd message
GET /engine.io/?EIO=4&transport=websocket&sid=lv_VI97HAXpY6yYWAAAC
< HTTP/1.1 101 Switching Protocols
WebSocket frames:
< 2probe => probe request
> 3probe => probe response
< 5 => "upgrade" packet type
> 4hello => message (not concatenated)
> 4world
> 2 => "ping" packet type
< 3 => "pong" packet type
> 1 => "close" packet type
在这个例子中,客户端只开启了 WebSocket 传输通道 (without HTTP polling).
GET /engine.io/?EIO=4&transport=websocket
< HTTP/1.1 101 Switching Protocols
WebSocket frames:
< 0{"sid":"lv_VI97HAXpY6yYWAAAC","pingInterval":25000,"pingTimeout":5000} => handshake
< 4hey
> 4hello => message (not concatenated)
> 4world
< 2 => "ping" packet type
> 3 => "pong" packet type
> 1 => "close" packet type
Engine.IO url 包含了以下内容
/engine.io/[?<query string>]
有两种不同类型的编码
一个编码的数据包可以是 UTF-8 字符串或者二进制数据。字符串的数据包编码格式如下:
<packet type id>[<data>]
example:
4hello
对于二进制数据,不包括数据包类型(packet type),因为只有 “message” 数据包类型可以包括二进制数据。
新传输通道建立的时候,从服务端发送 Sent from the server when a new transport is opened (recheck)
请求关闭此传输,但不关闭连接本身。
由服务器发送。客户应该用 pong 数据包应答。
example
server sends: 2
client sends: 3
3 pong
由客户端发送以响应 ping 数据包。
4 message
实际传输的消息
example 1
server sends: 4HelloWorld
client receives and calls callback socket.on('message', function (data) { console.log(data); });
example 2
client sends: 4HelloWorld
server receives and calls callback socket.on('message', function (data) { console.log(data); });
5 upgrade
在 engine.io 切换传输通道之前,它测试服务器和客户端是否可以通过该传输进行通信。如果此测试成功,客户端将发送一个升级包,请求服务器刷新旧传输上的缓存,并切换到新传输通道。
6 noop
一个 noop 包。主要用于建立 websocket 连接之后关闭长轮询。
example
client connects through new transport
client sends 2probe
server receives and sends 3probe
client receives and sends 5
server flushes and closes old transport and switches to new.
Payload 是捆绑在一起的一系列 encoded packets。Payload 编码格式如下:
<packet1>\x1e<packet2>\x1e<packet3>
数据包分割符使用 record separator ('\x1e'). 更多可参考: https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Field_separators 当有效负载中包含二进制数据时,它将作为 base64 编码字符串发送。为了解码的目的,将标识符 b 置于包含二进制数据的分组编码之前。可以发送任意数量的字符串和 base64 编码字符串的组合。下面是 base 64 编码消息的示例:
<packet1>\x1eb<packet2 data in b64>[...]
Payload 用于不支持帧的传输通道,例如轮询协议。
不包含二进制的例子:
[
{
"type": "message",
"data": "hello"
},
{
"type": "message",
"data": "€"
}
]
编码后:
4hello\x1e4€
包含二进制的例子:
[
{
"type": "message",
"data": "€"
},
{
"type": "message",
"data": buffer <01 02 03 04>
}
]
编码后:
4€\x1ebAQIDBA==
分解:
4 => "message" packet type
€
\x1e => record separator
b => indicates a base64 packet
AQIDBA== => buffer content encoded in base64
engine.io server 必须支持三种传输通道:
轮询传输包括客户端向服务器发送周期性 GET 请求以获取数据,以及将带有有效负载的请求从客户端发送到服务器以发送数据。
服务器必须支持 CORS 响应。
服务器实现必须使用有效的 JavaScript 进行响应。在响应中需要使用 URL 中 query 中的 j 参数。j 是一个整数。
JSONP 数据包的格式。
`___eio[` <j> `]("` <encoded payload> `");`
为了确保 payload 得到正确处理,需要对 payload 进行转义,使得响应体是一个合法的 JavaScript。
服务器返回的 JSONP 数据帧的例子
___eio[4]("packet data");
Posting data
客户端通过隐藏的 iframe 发送数据。数据以 URI 编码格式发送给服务器,如下所示
d=<escaped packet payload>
除了常规的 qs 转义之外,为了防止浏览器处理的不一致,\n 在被 POSTd 之前将被转义为 \n。
客户端使用 EventSource 对象接收数据,使用 XMLHttpRequest 对象发送数据。
上面的对 payloads 的编码方式并不用于 WebSocket 通道,WebSocket 通道本身已有轻量级的数据帧机制。
发送消息的时候,对数据包进行单独编码,然后依次调用 send () 进行发送。
连接总是以轮询(XHR 或 JSONP)开始。WebSocket 通过发送探针在侧面进行测试 (2probe)。如果探测由服务器响应 (3probe),则客户端会发送一个升级包 (5)。
为了确保没有消息丢失,只有在刷新现有传输的所有缓冲区并认为传输已暂停后,才会发送升级数据包。
当服务器收到升级包时,它必须假定这是新的传输通道,并将所有现有缓冲区(如果有的话)发送给它。
客户端发送的探测器是一个 ping+probe 作为数据发送。(2probe) 服务端发送的探测器是一个 pong+probe 作为数据发送。(3probe)
客户端必须使用握手中发送的 pingTimeout 和 pingInterval 来确定服务器是否无响应。
服务器发送一个 ping 数据包。如果在 pingTimeout 内未收到任何数据包类型,服务器将认为套接字已断开连接。如果收到了 pong 数据包,服务器将在等待 pingInterval 之后再次发送 ping 数据包。
由于这两个值在服务器和客户端之间共享,当客户端在 pingTimeout+pingInterval 内没有接收到任何数据时,客户端也能探测到服务器是否变得无响应。
const engine = require('engine.io');
const server = engine.listen(3000,{
cors: {
origin: "*"
}
});
server.on('listen', () => {
console.log('listening on 3000')
})
server.on('connection', socket => {
console.log('new connection')
socket.send('utf 8 string');
socket.send(Buffer.from('hello world')); // binary data
});
const { Socket } = require('engine.io-client');
const socket = new Socket('ws://localhost:3000');
socket.on('open', () => {
socket.emit('message from client')
socket.on('message', (data) => {
console.log('receive message: ' + data);
socket.send('ack from client.');
});
socket.on('close', (e) => {
console.log('socket close',e)
});
});
1、Polling 传输通道握手
Request:
Response:
2、发起长轮询请求服务端数据
Request:
Response:
3、POST 方式发送数据到服务端
Request:
Request payload:
Response:
4、服务端告诉客户端传输通道已升级,回复一个 6
Request:
Response:
5、WebSocket 通道建立之后,切换为 WebSocket 传输数据
Connect:
Message:
const socket = new Socket('ws://localhost:3000',{ transports: ['websocket'] } );
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/bemT3Gz7xiLuHDwB5hMsYQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。