Socket.IOでリアルタイム双方向通信

Socket.IOは、双方向通信を実現してくれるNode.jsのライブラリです。ここでは、簡単なプログラムを通じて双方向通信の動作確認をします。(Client側もNode.jsを利用しており、実装はTypeScriptで行っています。)

WebSocketについて

WebSocketは、双方向通信を行うためのプロトコルです。通信のたびに新しくコネクションを確立する必要がなく、HTTPに比べて軽量なヘッダなので、双方向通信を低コストで実現できます。

ws:wss: のURIスキームが定義されています。wss: はTLSで暗号化されます。

ブラウザサポート状況は以下サイトで確認できます。
https://caniuse.com/websockets

Socket.IOについて

Socket.IOは、WebSocketの実装というわけではないですが、状況に合わせて WebSocketロングポーリング など利用して双方向通信を実現してくれます。

前準備

必要パッケージをインストールします。今回、Client側もnode.jsで確認するので socket.io-client もインストールします。

npm init
npm install socket.io socket.io-client

処理は TypeScript で記述します。

npm install --save-dev typescript
npx tsc --init

動作確認1|接続

まずは、接続だけ行います。

server.ts

import http from 'http';
import socketio from 'socket.io';

const server: http.Server = http.createServer();
const io: socketio.Server = new socketio.Server(server);

io.on('connection', (socket: socketio.Socket) => console.log('connect'));

const port = 5000;
server.listen(port, () => console.log(`app listening on port ${port}`));

client.ts

import { io } from 'socket.io-client';

const port = 5000;
const socket = io(`http://localhost:${port}`);

socket.on('connect', () => console.log('connect'));

動作確認

760-nodejs-socketio_00.png

server.tsだけ起動しています。

760-nodejs-socketio_01.png

client.tsも起動して接続されました。

動作確認2|Server - Client間で通信
( emit )

双方間通信を確認します。

server.ts

import http from 'http';
import socketio from 'socket.io';

const server: http.Server = http.createServer();
const io: socketio.Server = new socketio.Server(server);

io.on('connection', (socket: socketio.Socket) => {
  console.log('connect');

  let counter = 0;

  // Clientからメッセージを受信
  socket.on('yyy', (data: { message: string }) => {
    console.log(`type: ${typeof data}   data: ${data.message}`);
  });

  // Clientにメッセージを送信
  setInterval(() => {
    socket.emit('xxx', { message: `server message ${counter++}` });
  }, 1000);
});

const port = 5000;
server.listen(port, () => {
  console.log(`app listening on port ${port}`);
});

client.ts

import { io } from 'socket.io-client';

const port = 5000;
const socket = io(`http://localhost:${port}`);

socket.on('connect', () => {
  console.log('connect');
});

// Serverからメッセージを受信
socket.on('xxx', (data: { message: string }) => {
  console.log(`type: ${typeof data}   data: ${data.message}`);
});

// Serverにメッセージを送信
let counter = 0;
setInterval(() => {
  socket.emit('yyy', { message: `client message ${counter++}` });
}, 1000);

補足

760-nodejs-socketio_10.png

serverからclientに xxx eventを通じてメッセージを送信しています。
clientからserverに yyy eventを通じてメッセージを送信しています。

動作確認

760-nodejs-socketio_11.png

双方向の通信を確認できました。

動作確認2|任意の範囲内で通信
( room )

roomという機能が提供されており、roomにjoinしている範囲内で通信できます。

Rooms | Socket.IO

server.ts

import http from 'http';
import socketio from 'socket.io';

const server: http.Server = http.createServer();
const io: socketio.Server = new socketio.Server(server);

io.on('connection', (socket: socketio.Socket) => {
  const socketId = socket.id;
  console.log(`[connect] socketId: ${socketId}`);

  let clientId = '';
  let roomId = '';
  let counter = 0;

  // Clientからのメッセージを受信
  socket.on('join_to_room', (data: { clientId: string; roomId: string }) => {
    roomId = data.roomId;
    clientId = data.clientId;
    socket.join(roomId);
    console.log(
      `[join to room] socketId: ${socketId} clientId: ${clientId} roomId: ${roomId}`,
    );
  });

  // Clientにメッセージを送信
  setInterval(() => {
    socket.to(roomId).emit('server_to_client', {
      message: {
        socketId,
        clientId,
        roomId,
        counter: counter++,
      },
    });
  }, 1000);
});

const port = 5000;
server.listen(port, () => {
  console.log(`app listening on port ${port}`);
});

client.ts

実行時、引数で clientId roomId を指定できるようにしています。

import { io } from 'socket.io-client';

const port = 5000;
const socket = io(`http://localhost:${port}`);

const clientId = process.argv[2];
const roomId = process.argv[3];
console.log(`clientId: ${clientId}  roomId: ${roomId}`);

socket.on('connect', () => {
  console.log('connect');
});

// Serverからメッセージを受信
socket.on('server_to_client', (data: { message: object }) => {
  console.log(JSON.stringify(data.message));
});

// Serverにメッセージを送信
socket.emit('join_to_room', { clientId, roomId });

補足

760-nodejs-socketio_20.png

serverは、clientから join_to_room eventを通じてメッセージを受信したタイミングで roomjoin します。
serverは、room を指定したうえで、 server_to_client eventを通じてclientにメッセージを送信しています。

動作確認

1. server.tsを起動

760-nodejs-socketio_21.png

2. client.tsを起動(ClientA)

760-nodejs-socketio_22.png

3. client.tsを起動(ClientB)

ClientAとは異なるroomIdを指定して起動します。

760-nodejs-socketio_23.png

roomId が異なるのでメッセージは表示されません。

4. client.tsを起動(ClientC)

ClientAとは同じ roomId を指定して起動します。

760-nodejs-socketio_24.png

ClientAとClientCで roomId が同じなので、ClientAの画面にはClientCのメッセージが表示され、ClientCの画面にはClientAのメッセージが表示されています。

参考

WebSocket関連

Socket.IO関連