WebSocket でメッセージを送受信する

Recipe ID: net-010

WebSocket 接続が確立された後に、テキストまたはバイナリデータのメッセージを送受信する方法を解説します。
Tauri の plugin-websocket は、テキストメッセージ(String)とバイナリメッセージ(Array of numbers / Uint8Array)の両方に対応しています。

前提条件

この機能を使用するには、@tauri-apps/plugin-websocket プラグインが必要です。

1. プラグインのインストール

npm run tauri add websocket

2. Permissions (権限) の設定

src-tauri/capabilities/default.jsonwebsocket:default 権限を追加します。
Tauri v2 ではセキュリティの観点から allow: [{ "url": "*" }] のような無制限なワイルドカード指定は許可されていない場合があるため、接続する WebSocket サーバーの URL を明示的に指定する必要があります。

以下の例では、コード例で使用する localhost:8080 を許可しています。

{
  "permissions": [
    "websocket:default",
    {
        "identifier": "websocket:allow-connect",
        "allow": [
            { "url": "ws://localhost:8080" }
        ]
    }
  ]
}
注意: 本番ビルドでは実際に使用するドメインのみを許可することが強く推奨されます。

使用方法

メッセージの送信 (send)

send メソッドを使用してサーバーへデータを送信します。引数には文字列または数値の配列(バイナリデータ)を渡すことができます。

// テキスト送信
await ws.send('Hello WebSocket!');

// バイナリ送信 (例: [0x00, 0x01, 0x02])
await ws.send([0, 1, 2]);

メッセージの受信 (addListener)

addListener メソッドでコールバック関数を登録します。
受信した Message オブジェクトには、データの種類を示す type と、データ本体の data が含まれます(Tauri v2 の仕様詳細に依存しますが、一般的に型判別が可能です)。

ws.addListener((msg) => {
    // msg の構造はバージョンにより異なる場合がありますが、基本的には以下のように扱います
    console.log('Received:', msg);
});

コード例

例1: チャットメッセージの送受信

JSON 形式で構造化されたデータを送受信する実用的な例です。

import WebSocket from '@tauri-apps/plugin-websocket';

async function setupChatClient(): Promise<(text: string) => Promise<void>> {
    const ws = await WebSocket.connect('ws://localhost:8080/chat');

    // 受信ハンドラ
    ws.addListener((message: any) => {
        // v2 プラグインのメッセージ形式に合わせてパース
        // もし message が文字列で直接来る場合:
        // const data = JSON.parse(message);
        
        // もし message が { type: 'Text', data: '...' } のようなオブジェクトの場合:
        if (typeof message === 'string') {
             handleData(JSON.parse(message));
        } else if (message.type === 'Text') {
             handleData(JSON.parse(message.data as string));
        }
    });

    // 送信関数
    async function sendMessage(text: string) {
        const payload = {
            user: 'TauriUser',
            content: text,
            timestamp: Date.now()
        };
        await ws.send(JSON.stringify(payload));
    }

    return sendMessage;
}

function handleData(data: any) {
    console.log(`[${data.user}]: ${data.content}`);
}

例2: バイナリデータの送受信

画像データなどのバイナリをやり取りする例です。

import WebSocket from '@tauri-apps/plugin-websocket';

async function handleBinaryData(): Promise<void> {
    const ws = await WebSocket.connect('ws://localhost:8080/binary');

    ws.addListener((msg: any) => {
        // バイナリメッセージの判定(実装依存: msg.type === 'Binary' など)
        if (Array.isArray(msg) || msg instanceof Uint8Array) {
            console.log('Received binary data:', msg.length, 'bytes');
            // 必要に応じて Uint8Array に変換して処理
        } else if (msg.type === 'Binary') {
            const bytes = msg.data;
            console.log('Recieved binary:', bytes);
        }
    });

    // バイナリデータの送信
    const buffer = new Uint8Array([1, 2, 3, 4, 5]);
    // 配列に変換して送信 (プラグインが Uint8Array を直接受け付けない場合)
    await ws.send(Array.from(buffer));
}

注意点

* データ型の確認: サーバーから送られてくるデータがテキストなのかバイナリなのか、またプラグインがそれをどのようにラップして渡してくるか(生の文字列か、型情報付きオブジェクトか)は、プラグインのバージョンによって挙動が微細に異なる可能性があります。開発時には必ず console.log で受信メッセージの構造を確認してください。
* 非同期処理: ws.send() は Promise を返します。送信の完了(ネットワークへの書き込み完了)を待ちたい場合は await してください。