どのようにノードをリアルタイムでチャットアプリケーションを構築する.JSとソケット.io


多くのアプリケーションは、ユーザーに値を提供するためにリアルタイムデータ配送に頼ります.インスタントメッセージングから、オンラインマルチプレイヤーゲーム、IOT、広告サーバー、および株式交換ブローカー、1ポイントまたは別の時点であなたのような何かを構築するために挑戦する可能性があります.とノードで.JSとソケット.ああ、それはあなたが今それを学ぶことができるように簡単になった.
しかし、我々はアプリを構築を開始する前に、いくつかのトピックとは、リアルタイムのアプリケーションを構築するために使用できるアプローチについて話しましょう.

定期投票


あなたが定期的に(10秒ごとに言ってみましょう)のようなサーバーを要求するようなアプリケーションを送信するアプリケーションを持っているならば、これを要約するのに良い、簡単な方法です.今、これはいくつかの状況で動作することができますが、何百ものクライアントが数秒ごとにリクエストのこの量でサーバーを爆撃し続けた場合、何が起こるかを想像することができます.そこに行って、とてもストレスの多い日でした.

ロングポーリング


長いポーリングは、通常のポーリングに似ていますが、サーバーへの要求の後に、接続がハングアップし、サーバーは、新しい情報が一度だけ応答との接続を閉じることになります.応答を受信した後、クライアントはすぐに新しい情報を待っている新しい要求を送信します.これは遅れなくメッセージを伝えるための良いオプションです、しかし、サーバー・アーキテクチャは複数の未定の接続を扱うことができなければなりません.使用される技術の種類によって、それぞれの保留中の接続は多くのメモリを取ることができます.

Websocket


定期的なポーリングと長いポーリングがHTTPを利用する間、WebSocketはサーバーとクライアントの双方向通信を可能にするもう一つのコミュニケーション・プロトコルです.接続が開いた後、クライアントcan send messages to the server, and receive event-driven responses without having to poll the server for a reply .

ソケット。io


彼らのウェブサイトで、それは言います:「ソケット.IOはリアルタイム、双方向およびイベントベースの通信を可能にします.できればWebSocket接続を確立しようとしますが、そうでなければHTTPロングポーリングに戻ります.それはあなたがその上に何かを構築しようとしているときに考慮する重要な区別です.
彼らのウェブサイトはまた、ソケットをうまく利用するアプリケーションの例をリストします.IOのようなリアルタイムの分析は、クライアントに(カウンター、グラフやログのような)またはインスタントメッセージングやチャット(私たちが何をしているか)とドキュメントを編集するユーザーがリアルタイムで他のユーザーの変化を見ることができるドキュメントのコラボレーション(Google Docsを考えて)ドキュメントをプッシュします.一つは、ゲームがマルチプレイヤーデータを送受信するためにこの技術を利用する方法を考えることもできます.
それは信じられないほど簡単にノードに統合することです.JSアプリケーション(彼らはすべてのプラットフォームで動作すると言いますが、私は試していません).

始めましょう🚀


これはチャットアプリケーションがこのチュートリアルの最後に見えるものです.
チャットアプリケーションのスクリーンショット
ノードを必要とすることは言うまでもない.JSがインストールされているので、まだ持っていない場合はwebsite そして、少なくともLTSバージョンをダウンロードしてください.
NPMはノードパッケージマネージャです.私は糸を好む(そしてそれは私がチュートリアルを通して使用されるものです)が、必要に応じてNPMを使用して自由に感じる.これにより、アプリケーションファイルを保存するフォルダを作成します.次に、端末を開き、新しく作成したフォルダに移動します.cd realtime-chat ) ランyarn init -y すぐにパッケージを作成します.JSONファイルと必要な唯一の依存関係を追加することができます.yarn add socket.io .
今、我々は、ユーザーがチャットとノードを使用できるようになりますアムHTMLページが必要です.jsサーバー.だから、先に行くとindex.htmlserver.js ファイル.
これでパッケージを開けましょう.JSONといくつかの行を編集します.まず、変更しましょうmain からindex.js to server.js , とscripts テストスクリプトを削除して"start": "node server.js" これは私たちが実行できるようになりますyarn start アプリケーションのルートフォルダからサーバーを起動します.あなたのパッケージのその部分.JSONは次のようになります.
main: server.js,
scripts: {
  start: node server.js
}

インターフェース


HTMLはここではフォーカスされていないので、先に行くことができますし、これをコピーするindex.html ファイル
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>RealTime</title>
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
</head>

<body>
  <div class="bg-white overflow-hidden overflow-hidden shadow rounded-lg px-4 py-4 sm:px-6 w-4/5 mx-auto mt-8">
    <h2 class="text-2xl leading-8 font-extrabold text-gray-900 sm:text-3xl sm:leading-9">
      Chat
    </h2>

    <div class="px-4 py-5 sm:p-6" id="message-box">
      <!-- Content goes here -->
    </div>

    <div class="border-t border-gray-200 px-4 py-4 sm:px-6">
      <form id="form" action="#" method="POST" class="grid grid-cols-1 row-gap-6">
        <div>
          <div class="mt-1 relative rounded-md shadow-sm">
            <input id="input" placeholder="Start typing..."
              class="form-input py-3 px-4 block w-full transition ease-in-out duration-150">
          </div>
        </div>
        <button type="submit"
          class="w-full inline-flex items-center justify-center px-6 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150">
          Send message
        </button>
      </form>
    </div>

    <div class="border-t border-gray-200 px-4 py-4 sm:px-6">
      <h3 class="px-4 py-4">Who's online:</h3>
      <ul id="peer-list"
        class="px-6 py-3 max-w-0 w-full whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
        <!-- Content goes here -->
      </ul>
    </div>

  </div>
</body>
</html>

これは、チャットアプリケーションの基本的な構造です.すべてのメッセージを表示するボックス、メッセージを入力するフォームとそれを送信するボタンがあります.重要な部分のすべては、後でJavaScriptでそれらを取得できるようにIDを持っています.ここで私はTailwindCSS それは良い高速に見えるように.

サーバ


さて、オープンserver.js 次の行を追加します.
const fs = require('fs');
const http = require('http');
const SocketIO = require('socket.io');

// Prepares HTML file to be served
const content = fs.readFileSync(__dirname + '/index.html', 'utf8');
const httpServer = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.setHeader('Content-Length', Buffer.byteLength(content));
  res.end(content);
})

// Creates socket.io connection
const io = SocketIO(httpServer);

// Handles "connect" event
io.on('connect', socket => {
    // Handles "message" event sent by client
  socket.on('message', data => {
        // Emits new message to every connected client
        io.emit('newMessage', {
            message: data
        })
    })
});

// Starts up server
httpServer.listen(3000, () => {
  console.log("🔥 Listening on http://localhost:3000");
})
これは、アプリケーションの基本的な機能に十分です.フレームワークを使って簡単にすることができますexpress , しかし、今のところ、古典的なノード.JSサーバーが十分です.それはindex.html ファイルを作成し、ソケットを作成します.14行目のIO接続.次に、クライアントから発信され、接続を処理する“Connect”イベントをリッスンするイベントリスニング機能を使用できます.あなた自身のイベントのキーワードを(“接続”のような)を作成することができますが、あなたは彼らがソケットによって実装されているものと競合するように使用すべきではないいくつかのキーワードがあることを心に留めておく必要があります.入出力いくつかの例では、接続、切断、再接続とエラーが含まれます.これらのイベント名の完全なリストを見つけることができますhere .
16行目では“message”というイベントを聞き、そのイベントで受信したデータを処理するためにコールバックを渡します.次に、18行目で、すべての接続されたソケットに「newmessage」という名前のイベントを発します.我々が聞いたことに注意してくださいsocket これは、個々のクライアントが接続されていると私たちはio これはソケットの一種のプールです.あなたはいつもこれを参照することができますemit cheatsheet あなたが持っているすべてのオプションを見るには、すべての接続されたソケットに対してイベントを放出するのではなく、エミッター、または“部屋”に送信したり、ソケットからソケットに送信します.
今、私は物事をより面白くし、クライアントにランダムな名前を割り当てるには、クライアントに接続し、チャットすることができます知っているので、すべてのクライアントにこれらの名前を送信します.以下を加えましょう:
const animals = [
  'fish',
  'cat',
  'tiger',
  'bear',
  'bull',
  'fox'
]

const colors = [
  'red',
  'green',
  'blue',
  'yellow',
  'purple',
  'pink'
]

/**
 * Generates a random name based on an animal and a color
 * 
 * @return {String}
 */
function randomName() {
  const color = colors[Math.floor(Math.random() * colors.length)]
  const animal = animals[Math.floor(Math.random() * animals.length)]

  return `${color}-${animal}`;
}

// Stores the names and ids of connected clients
const sockets = {}

// Creates socket.io connection
const io = SocketIO(httpServer);
次に、「接続」イベント処理の中で、いくつかの新しいハンドラを追加しましょう.
// Handles “connect” event
io.on('connect', socket => {
  sockets[socket.id] = randomName();
  socket.emit('name-generated', sockets[socket.id]);
  io.emit('update-peers', Object.values(sockets));

  // Handles “message” event sent by client
  socket.on('message', data => {
    // Emits new message to every connected client
    io.emit('newMessage', {
      sender: sockets[socket.id],
      message: data
    })
  });

  // Handles “disconnect” event
  socket.on('disconnect', () => {
    delete sockets[socket.id]
    io.emit('update-peers', Object.values(sockets))
  })
})
ここでは、基本的にクライアントが接続するのを待ちます、そして、我々は彼らのソケットIDにランダムな名前を割り当てて、彼らが彼らが誰であるかについてわかっているように、彼らの「ランダムな名前」を送ります.次に、接続されたソケット名のリストを送信します.また、切断イベントを処理する必要がありますので、誰かが切断した場合は、接続されているソケットのリストを更新し、ユーザーインターフェイスを更新するためにチャットで皆に送ってください.
それはサーバーに接続し、その魔法を行うことができますので、クール、今クライアントを実装しましょう.

クライアント


に移動index.html ファイルとbodyタグを閉じる前に、次のように追加します.
<script src="/socket.io/socket.io.js"></script>
<script>

</script>

これはソケットを"import "するでしょう.IOスクリプト(より複雑なアプリケーションを構築して、モジュールバンドルを使用しているとき、これはおそらく別のJavascriptファイルで起こるでしょうが、これはこの記事の範囲外です).
スクリプトを通して使ういくつかの要素にアクセスしてプログラムを起動しましょう.
<script>
    const form = document.getElementById('form');
    const input = document.getElementById('input');
    const msgBox = document.getElementById('message-box');
    const peerList = document.getElementById('peer-list');
</script>
さて、ソケットを利用するために.IOを呼び出して変数に格納する必要があります.
<script>
  const form = document.getElementById('form');
  const input = document.getElementById('input');
  const msgBox = document.getElementById('message-box');
  const peerList = document.getElementById('peer-list');

  const socket = io();

  // Handles the "name-generated" event by storing the client's name in a variable
  socket.on('name-generated', () => ());

  // Handles the "update-peers" event by updating the peers list
  socket.on('update-peers', () => ());

  // Handles "newMessage" event and add that message to the chat
  socket.on('newMessage', () => ());

</script>
上記のすべてのイベントsocket.on() いくつかの時点でサーバから発信されますが、まだ実装されていません(つまり、これらのイベントを聞いた後では何もしませんが、すぐに行います).
/**
 * Retrieves message from input and emits to the server
 * 
 * @param {Object} evt Event fired by the form submission
 */
function submitHandler(evt) {
  evt.preventDefault();
  socket.emit('message', input.value);
  input.value = ''
  msgBox.focus();
}

form.addEventListener('submit', submitHandler)

ここでは、イベントリスナーをフォームに添付します.それは“Submit”のイベントとsubmitHandler デフォルトでは、フォームがページリロードをトリガーしないか、action そして、入力フィールド値を含む「メッセージ」イベントを発します.その後、フィールドをクリアし、フィールドではない何かに集中するので、ユーザーがモバイルデバイスにいる場合、キーボードは離れて行く.
さて、他のソケットのイベントリスナーに戻りましょう.まず最初に、最も簡単なものは、「名前生成」イベントを聞きます.覚えているなら、これはクライアントのためのランダムな名前を生成した後にサーバーが発するイベントです.この名前を他の関数で使う必要があるので、以下のようなソケットリスナーと同じスコープで変数を作成しましょう.
let myName = ''
const socket = io();

// Handles the “name-generated” event by storing the client’s name in a variable
socket.on('name-generated', name => {
  myName = name
});

そして今、「newmessage」イベントを扱いましょう.ソケットが「メッセージ」イベントを発するときはいつでも、このイベントはサーバーによって発されます.言い換えると、誰かがサーバにメッセージを送り、サーバはこのメッセージを接続している人にブロードキャストします.
// Handles “newMessage” event and add that message to the chat
socket.on('newMessage', ({ sender, message }) => {
  let name = document.createElement('strong');
  name.textContent = `${sender} says: `

  let msgEl = document.createElement('span');
  msgEl.textContent = message

  let paragraph = document.createElement('p');
  paragraph.appendChild(name);
  paragraph.appendChild(msgEl);

  msgBox.appendChild(paragraph);
});
ここでは、サーバーがメッセージと送付者の名前を含んでいるオブジェクトを送ると思っています.私たちはこの情報を使用して、次のようなパラグラフ要素を作成します.そして、この段落をメッセージボックスに追加します.
オンラインクライアントのリストを実装してこれを終了しましょう.
// Handles the “update-peers” event by updating the peers list
socket.on('update-peers', peers => {
  peerList.innerHTML = ''

  const template = `<li class=“flex items-center space-x-3 lg:pl-2”>
    <div class=“flex-shrink-0 w-2 h-2 rounded-full bg-%PEER_COLOR%-600”></div>
      <span>%PEER_NAME%</span>
  </li>`

  for (const peer of peers) {
    let name = peer
    if (name === myName) {
      name += ' (you)'
    }
    peerList.innerHTML += template.replace('%PEER_NAME%', name).replace('%PEER_COLOR%', peer.split('-')[0])
  }
});
これはちょっと複雑に思えるかもしれませんが、私たちは「アップデートピア」イベントを聞いたときはいつでもオンラインピアのリストをクリアして、それを使ってmyName 変数は、それはあなたの表示を追加します.
そして、それ!あなたが走るならばyarn start あなたの端末でhttp://localhost:3000 あなたは、あなたが他のブラウザのウィンドウ、タブまたはデバイスと接続する場合は、チャットページを表示する必要があります接続のユーザーの成長のリストが表示されます.そして、あなたがそれらのウィンドウを閉じる場合は、チャットを残して、リストも更新されます.
私はこの記事が好きで、あなたのベルトの下にこの新しいツールで素晴らしいアプリケーションを作成します🍻