百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>JS教程> Node和React中怎样停止实时通讯?
分享文章到:

Node和React中怎样停止实时通讯?

发布时间:09/01 来源:未知 浏览: 关键词:

教程引荐:node js教程、React教程、WebSocket教程

Web 为了支撑客户端和效劳器之间的全双工(或双向)通讯已经走过了很长的路。这是 WebSocket 和谈的主要目的:通过单个 TCP 套接字连接在客户端和效劳器之间供给耐久的实时通讯。

WebSocket 和谈只要两个议程:1)翻开握手,2)帮忙数据传输。一旦效劳器和客户端握手成功,他们就可以随便地以较少的开销彼此发送数据。

WebSocket 通讯使用WS(端口80)或WSS(端口443)和谈在单个 TCP 套接字上停止。按照 Can I Use,撰写本文时除了 Opera Mini 之外几乎所有的阅读器支撑 WebSockets 。

近况

从历史上看,创立需要实时数据通讯(如游戏或谈天利用程序)的 Web 利用需要滥用 HTTP 和谈来创立双向数据传输。尽管有很多种办法用于实实际时功效,但没有一种办法与 WebSockets 一样高效。 HTTP 轮询、HTTP流、Comet、SSE —— 它们都有本人的缺陷。

HTTP 轮询

解决问题的第一个尝试是按期轮询效劳器。 HTTP 长轮询生命周期如下:

  1. 客户端发出恳求并不断等候响应。
  2. 效劳器延迟响应,直到发生更换、更新或超时。恳求保持“挂起”,直到效劳器有东西返回客户端。
  3. 当效劳器端有一些更换或更新时,它会将响应发送回客户端。
  4. 客户端发送新的长轮询恳求以侦听下一组更换。

长轮询中存在许多破绽 —— 标头开销、延迟、超时、缓存等等。

HTTP 流式传输

这种机制减少了网络延迟的疾苦,由于初始恳求无穷期地保持翻开状态。即便在效劳器推送数据之后,恳求也永久不会终止。 HTTP 流中的前三步生命周期办法与 HTTP 轮询是雷同的。

但是,当响应被发送回客户端时,恳求永久不会终止,效劳器保持连接翻开状态,并在发生更换时发送新的更新。

效劳器发送事件(SSE)

使用 SSE,效劳器将数据推送到客户端。谈天或游戏利用不克不及完全依靠 SSE。 SSE 的完善用例是相似 Facebook 的新闻 Feed:每当有新帖公布时,效劳器会将它们推送到时间线。 SSE 通过传统 HTTP 发送,并且对翻开的连接数有限制。

这些办法不仅效力低下,保护它们的代码也使开发人员感到厌倦。

WebSocket

WebSockets 旨在代替现有的双向通讯技术。当触及全双工实时通讯时,上述现有办法既不成靠也不高效。

WebSockets 相似于 SSE,但在将新闻从客户端传回效劳器方面也很优异。由于数据是通过单个 TCP 套接字连接供给的,因此连接限制不再是问题。


实战教程

正如介绍中所提到的,WebSocket 和谈只要两个议程。让我们看看 WebSockets 怎样实现这些议程。为此我将剖析一个 Node.js 效劳器并将其连接到使用 React.js 构建的客户端上。

议程1:WebSocket在效劳器和客户端之间创立握手

在效劳器级别创立握手

我们可以用单个端口来离别供给 HTTP 效劳和 WebSocket 效劳。下面的代码显示了一个简便的 HTTP 效劳器的创立历程。一旦创立,我们会将 WebSocket 效劳器绑定到 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
});

创立 WebSocket 效劳器后,我们需要在接收来自客户端的恳求时接受握手。我将所有连接的客户端作为对象留存在代码中,并在收请从阅读器发来的求时使用独一的会员ID。

// 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 和谈的第二个议程。

议程2:实时信息传输

2.gif

我将编写一个根本的实时文档编纂器,会员可以将它们连接在一起并编纂文档。我跟踪了两个事件:

  1. 会员活动:每次会员参加或分开时,我都会将新闻播送给所有连接其他的客户端。
  2. 内容更换:每次修改编纂器中的内容时,都会向所有连接的其他客户端播送。

该和谈同意我们用二进制数据或 UTF-8 发送和接收新闻(留意:传输和转换 UTF-8 的开销较小)。

只要我们对套接字事件onopenoncloseonmessage有了充分的理解,懂得和实现 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));
    }
  });

将新闻播送到所有连接的客户端。

4.gif

阅读器关闭后会发生什么?

在这种状况下,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。

编码欢乐!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有151人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板