Heim >Web-Frontend >js-Tutorial >Tauchen Sie mit JavaScript tief in Websockets und HTTP/2 mit SSE ein und erfahren Sie, wie Sie den richtigen Weg wählen!
Dies ist der fünfte Artikel in einer Reihe, die sich der Erforschung von JavaScript und den darauf aufbauenden Komponenten widmet.
Verwandte kostenlose Lernempfehlungen: Javascript (Video)
Dieses Mal werden wir tief in den Bereich der Kommunikationsprotokolle eintauchen, ihre Eigenschaften abbilden und erforschen und dabei einige Komponenten bauen . Schneller Vergleich zwischen WebSockets und HTTP/2. Abschließend geben wir einige Tipps zur Auswahl eines Netzwerkprotokolls.
Einführung
Heutzutage werden komplexe Webanwendungen mit einer umfangreichen, funktionsreichen und dynamischen Benutzeroberfläche als selbstverständlich angesehen. Das ist keine Überraschung – das Internet hat seit seiner Einführung einen langen Weg zurückgelegt.
Ursprünglich war das Internet nicht für die Unterstützung solch dynamischer und komplexer Webanwendungen ausgelegt. Dabei handelt es sich um eine Sammlung von HTML-Seiten, die miteinander verknüpft sind, um ein „Web“-Konzept mit Informationen zu bilden. Alles basiert auf dem sogenannten Request/Response-Paradigma von HTTP. Der Client lädt eine Seite und dann passiert nichts, bis der Benutzer klickt und zur nächsten Seite navigiert.
Etwa 2005 wurde AJAX eingeführt und viele Menschen begannen, die Möglichkeit zu erkunden, eine bidirektionale Verbindung zwischen dem Client und dem Server herzustellen. Dennoch wird die gesamte HTTP-Kommunikation vom Client gesteuert, was eine Benutzerinteraktion oder regelmäßige Abfragen erfordert, um neue Daten vom Server zu laden.
HTTP zu einer „wechselseitigen“ Interaktion machen
Die Technologie, die es dem Server ermöglicht, Daten „aktiv“ an den Client zu senden, gibt es schon seit geraumer Zeit. Zum Beispiel „Push“ und „Comet“.
Einer der häufigsten Hacks besteht darin, dem Server die Illusion zu vermitteln, dass er Daten an den Client senden muss. Dies wird als „langes Polling“ bezeichnet. Beim langen Polling öffnet der Client eine HTTP-Verbindung zum Server und hält diese offen, bis eine Antwort gesendet wird. Dies wird als Antwort gesendet, wenn der Server neue Daten zu senden hat. Schauen Sie sich an, wie ein sehr einfaches langes Polling-Code-Snippet aussieht:
(function poll(){ setTimeout(function(){ $.ajax({ url: 'https://api.example.com/endpoint', success: function(data) { // Do something with `data` // ... //Setup the next poll recursively poll(); }, dataType: 'json' }); }, 10000); })();
Dies ist im Grunde eine selbstausführende Funktion, die bei der ersten sofortigen Ausführung ein 10-Sekunden-Intervall zwischen Aufrufen an den Server nach dem asynchronen Ajax-Aufruf festlegt , ruft der Rückruf erneut Ajax auf.
Andere Technologien beinhalten Flash- oder XHR-Multipart-Requests und sogenannte HTML-Dateien.
Allerdings haben alle diese Arbeitsbereiche das gleiche Problem: Sie sind alle mit dem Overhead von HTTP ausgestattet, was sie für Anwendungen mit geringer Latenz ungeeignet macht. Denken Sie an einen In-Browser-Multiplayer-Ego-Shooter oder ein anderes Online-Spiel mit einer Echtzeitkomponente.
Einführung von WebSocketsDie WebSocket-Spezifikation definiert eine API zum Herstellen von „Socket“-Verbindungen zwischen Webbrowsern und Servern. Einfach ausgedrückt: Es besteht eine dauerhafte Verbindung zwischen Client und Server und beide Parteien können jederzeit mit dem Datenversand beginnen.
Der Client stellt über den WebSocket-Handshake-Prozess eine WebSocket-Verbindung her. Der Prozess beginnt damit, dass der Client eine reguläre HTTP-Anfrage an den Server sendet, die einen Upgrade-Header enthält, der den Server darüber informiert, dass der Client eine WebSocket-Verbindung herstellen möchte.
Der Client stellt eine WebSocket-Verbindung wie folgt her: // Create a new WebSocket with an encrypted connection.
var socket = new WebSocket('ws://websocket.example.com')
WebSocket-URL verwendet das ws-Schema. Es gibt auch wss für sichere WebSocket-Verbindungen, äquivalent zu HTTPS.
Diese Lösung ist nur der Anfang des Öffnens der WebSocket-Verbindung von websocket.example.com.
Hier ist ein vereinfachtes Beispiel der anfänglichen Anforderungsheader:Wenn der Server das WebSocket-Protokoll unterstützt, stimmt er dem Upgrade zu und teilt es über den Upgrade-Header in der Antwort mit.
Node.js-Implementierung:
Nachdem die Verbindung hergestellt wurde, antwortet der Server, indem er den Inhalt im Header aktualisiert: Sobald die Verbindung hergestellt ist, wird das offene Ereignis auf dem Client-WebSocket ausgelöst Beispiel:var socket = new WebSocket('ws://websocket.example.com'); // Show a connected message when the WebSocket is opened. socket.onopen = function(event) { console.log('WebSocket is connected.'); };Nachdem der Handshake abgeschlossen ist, wird die anfängliche HTTP-Verbindung durch eine WebSocket-Verbindung ersetzt, die dieselbe zugrunde liegende TCP/IP-Verbindung verwendet. An diesem Punkt können beide Parteien mit dem Senden von Daten beginnen.
使用 WebSockets,可以传输任意数量的数据,而不会产生与传统 HTTP 请求相关的开销。数据作为消息通过 WebSocket 传输,每个消息由一个或多个帧组成,其中包含正在发送的数据(有效负载)。为了确保消息在到达客户端时能够正确地进行重构,每一帧都以负载的4-12字节数据为前缀, 使用这种基于帧的消息传递系统有助于减少传输的非有效负载数据量,从而大大的减少延迟。
注意:值得注意的是,只有在接收到所有帧并重构了原始消息负载之后,客户机才会收到关于新消息的通知。
WebSocket URLs
之前简要提到过 WebSockets 引入了一个新的URL方案。实际上,他们引入了两个新的方案:ws:// 和wss://。
url 具有特定方案的语法。WebSocket url 的特殊之处在于它们不支持锚点(#sample_anchor)。
同样的规则适用于 WebSocket 风格的url和 HTTP 风格的 url。ws 是未加密的,默认端口为80,而 wss 需要TLS加密,默认端口为 443。
帧协议
更深入地了解帧协议,这是 RFC 为我们提供的:
在RFC 指定的 WebSocket 版本中,每个包前面只有一个报头。然而,这是一个相当复杂的报头。以下是它的构建模块:
FIN
:1bit ,表示是消息的最后一帧,如果消息只有一帧那么第一帧也就是最后一帧,Firefox 在 32K 之后创建了第二个帧。RSV1,RSV2,RSV3
:每个1bit,必须是0,除非扩展定义为非零。如果接受到的是非零值但是扩展没有定义,则需要关闭连接。Opcode
:4bit,解释 Payload 数据,规定有以下不同的状态,如果是未知的,接收方必须马上关闭连接。状态如下:
Mask
:1bit,掩码,定义payload数据是否进行了掩码处理,如果是1表示进行了掩码处理。Masking-key
:域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。Payload_len
: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
:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。为什么 WebSocket 是基于帧而不是基于流?我不知道,就像你一样,我很想了解更多,所以如果你有想法,请随时在下面的回复中添加评论和资源。另外,关于这个主题的讨论可以在 HackerNews 上找到。
帧数据
如上所述,数据可以被分割成多个帧。 传输数据的第一帧有一个操作码,表示正在传输什么类型的数据。 这是必要的,因为 JavaScript 在开始规范时几乎不存在对二进制数据的支持。 0x01 表示 utf-8 编码的文本数据,0x02 是二进制数据。大多数人会发送 JSON ,在这种情况下,你可能要选择文本操作码。 当你发送二进制数据时,它将在浏览器特定的 Blob 中表示。
通过 WebSocket 发送数据的API非常简单:
var socket = new WebSocket('ws://websocket.example.com'); socket.onopen = function(event) { socket.send('Some message'); // Sends data to server. };
当 WebSocket 接收数据时(在客户端),会触发一个消息事件。此事件包括一个名为data的属性,可用于访问消息的内容。
// Handle messages sent by the server. socket.onmessage = function(event) { var message = event.data; console.log(message); };
在Chrome开发工具:可以很容易地观察 WebSocket 连接中每个帧中的数据:
消息分片
有效载荷数据可以分成多个单独的帧。接收端应该对它们进行缓冲,直到设置好 fin
位。因此,可以将字符串“Hello World”发送到11个包中,每个包的长度为6(报头长度)+ 1字节。控件包不允许分片。但是,规范希望能够处理交错的控制帧。这是TCP包以任意顺序到达的情况。
连接帧的逻辑大致如下:
分片目的是发送长度未知的消息。如果不分片发送,即一帧,就需要缓存整个消息,计算其长度,构建frame并发送;使用分片的话,可使用一个大小合适的buffer,用消息内容填充buffer,填满即发送出去。
什么是跳动检测?
主要目的是保障客户端 websocket 与服务端连接状态,该程序有心跳检测及自动重连机制,当网络断开或者后端服务问题造成客户端websocket断开,程序会自动尝试重新连接直到再次连接成功。
在使用原生websocket的时候,如果设备网络断开,不会触发任何函数,前端程序无法得知当前连接已经断开。这个时候如果调用 websocket.send 方法,浏览器就会发现消息发不出去,便会立刻或者一定短时间后(不同浏览器或者浏览器版本可能表现不同)触发 onclose 函数。
后端 websocket 服务也可能出现异常,连接断开后前端也并没有收到通知,因此需要前端定时发送心跳消息 ping,后端收到 ping 类型的消息,立马返回 pong 消息,告知前端连接正常。如果一定时间没收到pong消息,就说明连接不正常,前端便会执行重连。
为了解决以上两个问题,以前端作为主动方,定时发送 ping 消息,用于检测网络和前后端连接问题。一旦发现异常,前端持续执行重连逻辑,直到重连成功。
错误处理
以通过监听 error 事件来处理所有错误:
var socket = new WebSocket('ws://websocket.example.com'); // Handle any error that occurs. socket.onerror = function(error) { console.log('WebSocket Error: ' + error); };
关闭连接
要关闭连接,客户机或服务器都应该发送包含操作码0x8
的数据的控制帧。当接收到这样一个帧时,另一个对等点发送一个关闭帧作为响应,然后第一个对等点关闭连接,关闭连接后接收到的任何其他数据都将被丢弃:
// Close if the connection is open. if (socket.readyState === WebSocket.OPEN) { socket.close(); }
另外,为了在完成关闭之后执行其他清理,可以将事件侦听器附加到关闭事件:
// Do necessary clean up. socket.onclose = function(event) { console.log('Disconnected from WebSocket.'); };
服务器必须监听关闭事件以便在需要时处理它:
connection.on('close', function(reasonCode, description) { // The connection is getting closed. });
WebSockets和HTTP/2 比较
虽然HTTP/2提供了很多功能,但它并没有完全满足对现有推送/流技术的需求。
关于 HTTP/2 的第一个重要的事情是它并不能替代所有的 HTTP 。verb、状态码和大部分头信息将保持与目前版本一致。HTTP/2 是意在提升数据在线路上传输的效率。
比较HTTP/2和WebSocket,可以看到很多相似之处:
正如我们在上面看到的,HTTP/2引入了 Server Push,它使服务器能够主动地将资源发送到客户机缓存。但是,它不允许将数据下推到客户机应用程序本身,服务器推送只由浏览器处理,不会在应用程序代码中弹出,这意味着应用程序没有API来获取这些事件的通知。
这就是服务器发送事件(SSE)变得非常有用的地方。SSE 是一种机制,它允许服务器在建立客户机-服务器连接之后异步地将数据推送到客户机。然后,只要有新的“数据块”可用,服务器就可以决定发送数据。它可以看作是单向发布-订阅模式。它还提供了一个名为 EventSource API 的标准JavaScript,作为W3C HTML5标准的一部分,在大多数现代浏览器中实现。不支持 EventSource API 的浏览器可以轻松地使用 polyfilled 方案来解决。
由于 SSE 基于 HTTP ,因此它与 HTTP/2 非常合适,可以结合使用以实现最佳效果:HTTP/2 处理基于多路复用流的高效传输层,SSE 将 API 提供给应用以启用数据推送。
为了理解 Streams 和 Multiplexing 是什么,首先看一下`IETF
定义:“stream”是在HTTP/2 连接中客户机和服务器之间交换的独立的、双向的帧序列。它的一个主要特征是,一个HTTP/2 连接可以包含多个并发打开的流,任何一个端点都可以从多个流中交错帧。
SSE basiert auf HTTP, was bedeutet, dass in HTTP/2 nicht nur mehrere SSE-Streams auf einer einzigen TCP-Verbindung verschachtelt werden können, sondern auch mehrere SSE-Streams (Server-zu-Client-Push) und mehrere Client-Anfragen (Client zum Server). Aufgrund von HTTP/2 und SSE gibt es nun eine reine HTTP-bidirektionale Verbindung und eine einfache API für die Registrierung von Anwendungscode beim Server-Push-Dienst. Beim Vergleich von SSE und WebSocket wird häufig das Fehlen bidirektionaler Fähigkeiten als großer Nachteil genannt. Mit HTTP/2 ist dies nicht mehr der Fall. Dadurch können Sie WebSocket überspringen und beim HTTP-basierten Signalisierungsmechanismus bleiben.
Wie wählt man zwischen WebSocket und HTTP/2?
WebSockets werden in der Welt von HTTP/2 + SSE überleben, vor allem weil es sich um eine Technologie handelt, die bereits gut etabliert ist und in einigen Fällen sehr spezifische Anwendungen hat hat Vorteile gegenüber HTTP/2, da es für bidirektionale Funktionalität mit weniger Overhead (z. B. Headern) entwickelt wurde.
Angenommen, Sie erstellen ein Massively-Multiplayer-Onlinespiel, das viele Nachrichten von beiden Enden der Verbindung erfordert. In diesem Fall schneiden WebSockets deutlich besser ab.
Verwenden Sie WebSockets im Allgemeinen immer dann, wenn Sie eine Verbindung zwischen Client und Server mit wirklich geringer Latenz und nahezu in Echtzeit benötigen. Dies erfordert möglicherweise ein Umdenken bei der Erstellung serverseitiger Anwendungen sowie eine Verlagerung des Fokus auf Ereignisse in der Warteschlange. usw. Technisch gesehen.
Verwenden Sie eine Lösung, die Marktnachrichten, Marktdaten, Chat-Anwendungen usw. in Echtzeit anzeigen muss. Wenn Sie sich auf HTTP/2 + SSE verlassen, erhalten Sie einen effizienten bidirektionalen Kommunikationskanal und profitieren gleichzeitig von den verschiedenen Vorteilen, die ein Aufenthalt im Internet mit sich bringt HTTP-Bereich:
Das obige ist der detaillierte Inhalt vonTauchen Sie mit JavaScript tief in Websockets und HTTP/2 mit SSE ein und erfahren Sie, wie Sie den richtigen Weg wählen!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!