>  기사  >  웹 프론트엔드  >  Node와 React에서 실시간으로 통신하는 방법은 무엇입니까?

Node와 React에서 실시간으로 통신하는 방법은 무엇입니까?

青灯夜游
青灯夜游앞으로
2020-09-12 10:55:064333검색

Node와 React에서 실시간으로 통신하는 방법은 무엇입니까?

추천 튜토리얼: node js 튜토리얼, React 튜토리얼, WebSocket 튜토리얼

웹은 클라이언트와 서버 간의 전이중(또는 양방향) 통신을 지원하기 위해 먼 길을 왔습니다. 이것이 WebSocket 프로토콜의 주요 목적입니다. 단일 TCP 소켓 연결을 통해 클라이언트와 서버 간의 지속적인 실시간 통신을 제공하는 것입니다.

WebSocket 프로토콜에는 1) 핸드셰이크 열기, 2) 데이터 전송 촉진이라는 두 가지 주제만 있습니다. 서버와 클라이언트 핸드셰이크가 성공적으로 완료되면 오버헤드가 거의 없이 자유롭게 서로에게 데이터를 보낼 수 있습니다.

WebSocket 통신은 WS(포트 80) 또는 WSS(포트 443) 프로토콜을 사용하여 단일 TCP 소켓을 통해 이루어집니다. Can I Use에 따르면, 작성 당시 Opera Mini를 제외한 거의 모든 브라우저는 WebSocket을 지원합니다.

현재 상황

역사적으로 실시간 데이터 통신이 필요한 웹 애플리케이션(예: 게임 또는 채팅 애플리케이션)을 만들려면 양방향 데이터 전송을 설정하기 위해 HTTP 프로토콜 남용이 필요합니다. 실시간 기능을 구현하는 방법에는 여러 가지가 있지만 WebSocket만큼 효율적인 방법은 없습니다. HTTP 폴링, HTTP 스트리밍, Comet, SSE - 모두 단점이 있습니다.

HTTP 폴링

문제를 해결하기 위한 첫 번째 시도는 주기적으로 서버를 폴링하는 것입니다. HTTP 긴 폴링 수명 주기는 다음과 같습니다.

  1. 클라이언트가 요청을 하고 응답을 기다립니다.
  2. 변경, 업데이트 또는 시간 초과가 발생할 때까지 서버는 응답을 지연합니다. 서버가 클라이언트에 무언가를 다시 보낼 때까지 요청은 "보류" 상태로 유지됩니다.
  3. 서버 측에 일부 변경 사항이나 업데이트가 있으면 클라이언트에 응답을 다시 보냅니다.
  4. 클라이언트는 다음 변경 사항을 수신하기 위해 새로운 긴 폴링 요청을 보냅니다.

긴 폴링에는 헤더 오버헤드, 대기 시간, 시간 초과, 캐싱 등 많은 취약점이 있습니다.

HTTP 스트리밍

이 메커니즘은 초기 요청이 무기한 열려 있기 때문에 네트워크 대기 시간의 고통을 줄여줍니다. 서버가 데이터를 푸시한 후에도 요청은 종료되지 않습니다. HTTP 흐름에서 수명 주기 메서드의 처음 세 단계는 HTTP 폴링과 동일합니다.

그러나 응답이 클라이언트로 다시 전송되면 요청은 종료되지 않으며 서버는 연결을 열어두고 변경 사항이 발생할 때 새 업데이트를 보냅니다.

서버 전송 이벤트(SSE)

SSE를 사용하여 서버는 클라이언트에 데이터를 푸시합니다. 채팅이나 게임 애플리케이션은 SSE에 전적으로 의존할 수 없습니다. SSE의 완벽한 사용 사례는 Facebook의 뉴스 피드와 같습니다. 새 게시물이 게시될 때마다 서버는 해당 게시물을 타임라인에 푸시합니다. SSE는 기존 HTTP를 통해 전송되며 열린 연결 수에 제한이 있습니다.

이러한 방법은 비효율적일 뿐만 아니라 이를 유지하기 위한 코드도 개발자에게 지치게 합니다.

WebSocket

WebSocket은 기존 양방향 통신 기술을 대체하도록 설계되었습니다. 위에서 설명한 기존 방법은 전이중 실시간 통신에 있어 신뢰성이 높지도 효율적이지도 않습니다.

WebSocket은 SSE와 유사하지만 클라이언트에서 서버로 메시지를 전달하는 데에도 탁월합니다. 데이터는 단일 TCP 소켓 연결을 통해 제공되므로 연결 제한은 더 이상 문제가 되지 않습니다.


실용 튜토리얼

서문에서 언급했듯이 WebSocket 프로토콜에는 두 가지 주제만 있습니다. WebSocket이 이러한 의제를 어떻게 구현하는지 살펴보겠습니다. 이를 위해 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*에서 생성된 값에 응답합니다. *Sec-WebSocket-Key*。服务器对此值进行编码和散列,并添加预定义的 GUID。它回应了服务器发送的握手中 *Sec-WebSocket-Accept*中生成的值。

一旦请求在服务器中被接受(在必要验证之后),就完成了握手,其状态代码为 101。如果在浏览器中看到除状态码 101

서버에서 요청이 승인되면(필요한 검증 후) 상태 코드 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:实时信息传输

Node와 React에서 실시간으로 통신하는 방법은 무엇입니까?

我将编写一个基本的实时文档编辑器,用户可以将它们连接在一起并编辑文档。我跟踪了两个事件:

  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(&#39;WebSocket Client Connected&#39;);
  };
  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(&#39;message&#39;, function(message) {
    if (message.type === &#39;utf8&#39;) {
      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));
    }
  });

将消息广播到所有连接的客户端。

Node와 React에서 실시간으로 통신하는 방법은 무엇입니까?

浏览器关闭后会发生什么?

在这种情况下,WebSocket调用 close 事件,它允许我们编写终止当前用户连接的逻辑。在我的代码中,当用户离开文档时,会向其余用户广播消息:

connection.on(&#39;close&#39;, 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제