🐥

Socket.IO サーバをローカル(node.js)で作成してPostman/HTMLそれぞれから接続してみる

に公開

先日こちらの記事でWebSocketのマネージドサービスを使ってPostmanから接続してメッセージを送受信(エコーバック)させてみました

用いたWebSocketのサービスはpieHostというものを使いました。
https://piehost.com/
こちらはサーバだけではなく無償でWebSocket/Socket.IOのテストがブラウザから簡単に行えるインターフェースを提供してくれています。(サーバとして対応しているのはWebSocketのみ)

そういえばWebSocketとSocket.IOの違いをなんとなくぼんやりとしか理解していなかったので、いい機会と思いSocket.IOのサーバを作成しつつ違いを把握してみることにしました。

WebSocket と Socket.IO

(以下調べて学んだこと)
WebSocketは RFC 6455定義された標準プロトコルで、一方Socket.IOはSocket.IOという組織によって独自に定義されたライブラリ、という違いがあるようです。Socket.IOはプロトコルとしてはWebSocketを用いますが、WebSocketが使えない場合HTTP通信を行うといったフォールバックメカニズムにより接続性などが向上しています。このためWebSocket非対応クライアントでもHTTPで接続が可能になります。
通常、WebSocketでアプリケーションを構築する場合は、セッション維持のための定期的なポーリングや再接続処理はアプリケーションレイヤで実装が必要ですが、Socket.IOでは自動で行ってくれることもメリットです。

一方WebSocketが標準プロトコルであるためブラウザから普通に使えるのに対して、Socket.IOは専用ライブラリが必要という制限もあるようです。
試したところ以下のHTMLで接続が行えます。

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO Test</title>
    <!-- Socket.IO クライアントライブラリ -->
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
  </head>
  <body>
    <h1>Socket.IO Test</h1>
    <script>
      const socket = io('http://localhost:3000');

      socket.on('connect', () => {
        console.log('Connected:', socket.id);
        socket.emit('message', 'Hello from Browser!');
      });

      socket.on('message', (msg) => {
        console.log('Server says:', msg);
      });
    </script>
  </body>
</html>

https://cdn.socket.io/4.7.5/socket.io.min.jsのプラグインを読み込めば通信が行えました。

さっそくっやってみる

ではテスト用Socket.IOサーバを起動してみます。
まずはプロジェクトを初期化します。

mkdir socketio-test
cd socketio-test
npm init -y

次に簡易的なHTTPサーバであるexpressとSocket.IOライブラリをインストールします。
(WebSocketもSocket.IOも両方HTTPベースで動作していることに変わりはないためです)

npm install express socket.io

server.jsというファイルを作成します。

server.js
// server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

// expressアプリを作成
const app = express();
// HTTPサーバを作成
const server = http.createServer(app);
// Socket.IOサーバを作成
const io = new Server(server, {
  cors: {
    origin: "*", // どこからでも接続を許可
  }
});

// ルートURLにアクセスがあったときの応答
app.get('/', (req, res) => {
  res.send('Socket.IO server is running!');
});

// Socket.IOの接続イベント
io.on('connection', (socket) => {
  console.log('A user connected: ' + socket.id);

  // クライアントからの'message'イベント受信
  socket.on('message', (msg) => {
    console.log('Message received: ' + msg);
    // エコーして返す
    socket.emit('message', 'Server Echo: ' + msg);
  });

  socket.on('disconnect', () => {
    console.log('User disconnected: ' + socket.id);
  });
});

// サーバ起動
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

これで準備は完成です。

node server.js

を実行しサーバを起動すると

Server is listening on port 3000

と表示されます。先ほどのhtmlファイルをブラウザで読み込むと以下の様に一意の接続識別子と受信したメッセージが表示されます。

A user connected: I99dH4c9IEEfjpTOAAAI
Message received: Hello from Browser!

Postmanからのテスト

いつも通りコレクションを作成します。


以下に様にメッセージを受信します。

A user connected: 9qvEWY-ucEu6sRRZAAAD
Message received: こんにちは from Postman

Serverの再起動時の挙動

PostmanとHTMLでそれぞれ接続がされた状態でserver側を先どうすると、再起動した瞬間にブラウザからメッセージを受信します。これは前述の通りHTMLベースのフォールバックメカニズムによる接続再試行が自動で行われているためです。一方Postmanからの接続がそれが行われないようPostman側で制御されています。サーバが停止した時点でPostman側は再接続を自動で行わず切断状態となります。

WebSocketによる接続

Socket.IOはWebSocketプロトコルに対応しているため当然WebScoketでの接続が可能です。上記のテストではhttpで試しましたが、ws://でも接続を行ってます。この際注意点ですが、普通のWebSocketクライアントではSocket.IOサーバにWebSocketで接続が行えません。Socket.IOライブラリを用いる必要があります。
このためPostmanのSocket.IOクライアント、WebSocketクライアント双方で今回の環境にはアクセスできませんでした。
先ほどテストで用いたHTMLをSocket.IO用ライブラリを用いたWebSocket接続用に変更したものが以下です。

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO Test</title>
    <!-- Socket.IO クライアントライブラリ -->
    <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
  </head>
  <body>
    <h1>Socket.IO Test</h1>
    <script>
  const socket = io('http://localhost:3000', {
    transports: ['websocket']
  });

      socket.on('connect', () => {
        console.log('Connected:', socket.id);
        socket.emit('message', 'Hello from Browser!');
      });

      socket.on('message', (msg) => {
        console.log('Server says:', msg);
      });
    </script>
  </body>
</html>

以下の部分がポイントです。

  const socket = io('http://localhost:3000', {
    transports: ['websocket']
  });

まずhttpで接続した後プロトコルを途中でWebSocketに変更しています。
ブラウザのデベロッパーツールでもステータスコード101:Switching Protocolが表示されていることが確認できます。

Discussion