#チュートリアルの推奨事項:Web は、クライアントとサーバー間の全二重 (または双方向) 通信をサポートするために長い道のりを歩んできました。これが WebSocket プロトコルの主な目的であり、単一の TCP ソケット接続を介してクライアントとサーバー間の永続的なリアルタイム通信を提供することです。 WebSocket プロトコルには 2 つのアジェンダしかありません。1) ハンドシェイクを開始すること、2) データ転送を支援することです。サーバーとクライアントのハンドシェイクが成功すると、ほとんどオーバーヘッドなく相互にデータを自由に送信できるようになります。 WebSocket 通信は、WS (ポート 80) または WSS (ポート 443) プロトコルを使用して、単一の TCP ソケット上で行われます。
Can I Use によると、この記事の執筆時点では、Opera Mini を除くほぼすべてのブラウザが WebSocket をサポートしています。
現在の状況これまで、リアルタイム データ通信を必要とする Web アプリケーション (ゲームやチャット アプリケーションなど) を作成するには、HTTP プロトコルを悪用して の 2 つを確立する必要がありました。データ転送の方法。リアルタイム機能を実装する方法は数多くありますが、WebSocket ほど効率的なものはありません。 HTTP ポーリング、HTTP ストリーミング、Comet、SSE - それらにはすべて欠点があります。
HTTP ポーリング問題を解決する最初の試みは、サーバーを定期的にポーリングすることです。 HTTP ロングポーリングのライフサイクルは次のとおりです。const webSocketsServerPort = 8000; const webSocketServer = require('websocket').server; const http = require('http'); // Spinning the http server and the websocket server. const server = http.createServer(); server.listen(webSocketsServerPort); const wsServer = new webSocketServer({ httpServer: server });
// I'm maintaining all active connections in this object const clients = {}; // This code generates unique userid for everyuser. const getUniqueID = () => { const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); return s4() + s4() + '-' + s4(); }; wsServer.on('request', function(request) { var userID = getUniqueID(); console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.'); // You can rewrite this part of the code to accept only the requests from allowed origin const connection = request.accept(null, request.origin); clients[userID] = connection; console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients)) });
それでは、接続が受け入れられると何が起こるでしょうか?
接続を確立するために通常の HTTP リクエストを送信するとき、クライアントはリクエスト ヘッダーで*Sec-WebSocket-Key* を送信します。サーバーはこの値をエンコードしてハッシュし、事前定義された GUID を追加します。これは、サーバーによって送信されたハンドシェイクの
*Sec-WebSocket-Accept* で生成された値で応答します。
101 で完了します。ブラウザにステータス コード
101 以外のものが表示された場合は、WebSocket のアップグレードが失敗し、通常の HTTP セマンティクスが適用されることを意味します。
*Sec-WebSocket-Accept*
头字段指示服务器是否愿意接受连接。此外如果响应缺少 *Upgrade*
头字段,或者 *Upgrade*
不等于 websocket
,则表示 WebSocket 连接失败。
成功的服务器握手如下所示:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols Connection: Upgrade Sec-WebSocket-Accept: Nn/XHq0wK1oO5RTtriEWwR4F7Zw= Upgrade: websocket
在客户端,我使用与服务器中的相同 WebSocket 包来建立与服务器的连接(Web IDL 中的 WebSocket API 正在由W3C 进行标准化)。一旦服务器接受请求,我们将会在浏览器控制台上看到 WebSocket Client Connected
。
这是创建与服务器的连接的初始脚手架:
import React, { Component } from 'react'; import { w3cwebsocket as W3CWebSocket } from "websocket"; const client = new W3CWebSocket('ws://127.0.0.1:8000'); class App extends Component { componentWillMount() { client.onopen = () => { console.log('WebSocket Client Connected'); }; client.onmessage = (message) => { console.log(message); }; } render() { return ( <div> Practical Intro To WebSockets. </div> ); } } export default App;
客户端发送以下标头来建立握手:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: vISxbQhM64Vzcr/CD7WHnw== Origin: http://localhost:3000 Sec-WebSocket-Version: 13
现在客户端和服务器通过相互握手进行了连接,WebSocket 连接可以在接收消息时传输消息,从而实现 WebSocket 协议的第二个议程。
我将编写一个基本的实时文档编辑器,用户可以将它们连接在一起并编辑文档。我跟踪了两个事件:
该协议允许我们用二进制数据或 UTF-8 发送和接收消息(注意:传输和转换 UTF-8 的开销较小)。
只要我们对套接字事件onopen
、onclose
和 onmessage
有了充分的了解,理解和实现 WebSockets 就非常简单。客户端和服务器端的术语相同。
在客户端,当新用户加入或内容更改时,我们用 client.send
向服务器发消息,以将新信息提供给服务器。
/* When a user joins, I notify the server that a new user has joined to edit the document. */ logInUser = () => { const username = this.username.value; if (username.trim()) { const data = { username }; this.setState({ ...data }, () => { client.send(JSON.stringify({ ...data, type: "userevent" })); }); } } /* When content changes, we send the current content of the editor to the server. */ onEditorStateChange = (text) => { client.send(JSON.stringify({ type: "contentchange", username: this.state.username, content: text })); };
我们跟踪的事件是:用户加入和内容更改。
从服务器接收消息非常简单:
componentWillMount() { client.onopen = () => { console.log('WebSocket Client Connected'); }; client.onmessage = (message) => { const dataFromServer = JSON.parse(message.data); const stateToChange = {}; if (dataFromServer.type === "userevent") { stateToChange.currentUsers = Object.values(dataFromServer.data.users); } else if (dataFromServer.type === "contentchange") { stateToChange.text = dataFromServer.data.editorContent || contentDefaultMessage; } stateToChange.userActivity = dataFromServer.data.userActivity; this.setState({ ...stateToChange }); }; }
在服务器中,我们只需捕获传入的消息并将其广播到连接到 WebSocket 的所有客户端。这是臭名昭着的 Socket.IO 和 WebSocket 之间的差异之一:当我们使用 WebSockets 时,我们需要手动将消息发送给所有客户端。 Socket.IO 是一个成熟的库,所以它自己来处理。
const sendMessage = (json) => { // We are sending the current data to all connected clients Object.keys(clients).map((client) => { clients[client].sendUTF(json); }); } connection.on('message', function(message) { if (message.type === 'utf8') { const dataFromClient = JSON.parse(message.utf8Data); const json = { type: dataFromClient.type }; if (dataFromClient.type === typesDef.USER_EVENT) { users[userID] = dataFromClient; userActivity.push(`${dataFromClient.username} joined to edit the document`); json.data = { users, userActivity }; } else if (dataFromClient.type === typesDef.CONTENT_CHANGE) { editorContent = dataFromClient.content; json.data = { editorContent, userActivity }; } sendMessage(JSON.stringify(json)); } });
将消息广播到所有连接的客户端。
在这种情况下,WebSocket调用 close
事件,它允许我们编写终止当前用户连接的逻辑。在我的代码中,当用户离开文档时,会向其余用户广播消息:
connection.on('close', function(connection) { console.log((new Date()) + " Peer " + userID + " disconnected."); const json = { type: typesDef.USER_EVENT }; userActivity.push(`${users[userID].username} left the document`); json.data = { users, userActivity }; delete clients[userID]; delete users[userID]; sendMessage(JSON.stringify(json)); });
该应用程序的源代码位于GitHub上的 repo 中。
WebSockets 是在应用中实现实时功能的最有趣和最方便的方法之一。它为我们提供了能够充分利用全双工通信的灵活性。我强烈建议在尝试使用 Socket.IO 和其他可用库之前先试试 WebSockets。
编码快乐!
更多编程相关知识,请访问:编程教学!!
以上がNode と React でリアルタイムに通信するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。