
リアルタイムチャットの仕組みをゼロから作ってみたい!そんなエンジニア向けに、この記事では Rust と WebSocket を活用して、複数クライアントが同じルーム内でメッセージをやり取りできる簡易的なチャットサーバーを構築する手順を紹介します。
Rustの非同期処理フレームワークである axum と tokio-tungstenite を使いながら、基本的な接続の実装からルーム管理、メッセージのブロードキャストまでをわかりやすく解説。Rust初心者でも安心して取り組める内容ですので、一緒にリアルタイム通信の世界を楽しんでみましょう!
本記事のゴール
axum と tokio-tungstenite を用いて、複数クライアントが同一のルームでメッセージを送り合う、簡易的なリアルタイムチャットサーバーを作成します。
前提知識と準備
WebSocket の基本概念を理解していること
Rust と Cargo がインストール済みであること
実装の流れ
1. プロジェクト作成と依存関係設定
まずはプロジェクトを作成。

Cargo.toml に必要な依存を追加します。

2. WebSocket 接続を受け付ける基本構造
最初は、WebSocket 接続を受け付けて、クライアントとやり取りする最小の構造を作ります。
この段階では、まだルームの概念やブロードキャストはありません。
ただ接続してメッセージを受け取り、そのまま送り返すだけの機能を実装します。
axum::extract::ConnectInfo は、接続情報を取得するための Extractor です。
使わなくても動作に影響はありませんが、接続元の IP アドレスなどを取得する際に便利です。

動作確認
1. サーバー起動

2. wscat で接続

3. テキストメッセージを送信

そのまま返ってくれば OK。
3. ルームとクライアント管理の仕組み
特定のルームに参加した複数クライアント間でメッセージを共有したい場合、接続状態をサーバー側で保持する必要があります。
今回は簡略化のため、データベースやキャッシュストア、PubSub システムなどを使わず、サーバーのメモリ上で管理します。
ここでは、以下の構造体を導入して、状態を管理します。
Room
文字列で識別されるチャット部屋
Peer
クライアントを識別する要素(SocketAddr で識別)
ユーザーの認証情報などを使用してクライアントを識別したほうがいいと思うけど、IP アドレスで代用しちゃう
PeerKey
(Room, Peer) のペア
特定のルームに所属するクライアントを識別するためのキー
AppState
サーバーの状態を保持する構造体
Arc<Mutex<HashMap<PeerKey, Peer>>> で、ルームとクライアントの紐づけを管理
DashMap などの並行処理に強い HashMap を使ったほうがいいと思うけど、Arc<Mutex<>> で代用しちゃう

AppState を main() で初期化し、Router に組み込みます。

4. メッセージの種類とルーム参加・退出
クライアントから
ルームに参加
ルームから退出
メッセージ配信
を表すペイロードを受け取り、イベントごとに処理を分岐させます。

この列挙型を使って、{"event":"join","room":"general"} のような JSON をパースして、ユーザーのリクエストを判別します。
5. WebSocket ハンドラでの参加・退出・ブロードキャスト処理
ここまでで、
WebSocket 接続を受け付ける基本構造
ルームとクライアント管理の仕組み
メッセージの種類とルーム参加・退出
が整いました。
次は、実際のハンドラ handle_socket 関数内で、参加・退出・メッセージ配信の機能を実装します。
ポイントは以下の通りです。
1. 受信タスク (recv_task) と送信タスク (send_task) の分離
受信処理と送信処理を独立した並行タスクとして管理し、コードの見通しを良くしておく
recv_task
クライアントからのメッセージを受け取り、イベント内容に応じてルームへの追加・削除・メッセージ配信を行うタスク
send_task
サーバーからクライアントへのメッセージ送信を行うタスク
全体像

recv_task(受信タスク)

send_task(送信タスク)

タスクの終了待ち合わせ

2. ルーム参加 (join) 処理
クライアントが {"event":"join","room":"general"} のようなイベントを送信した場合、そのクライアントを AppState の rooms に登録する
このクライアントにメッセージをブロードキャストできるよう、Room と SocketAddr の複合キーに紐づけておく

3. ルーム退出 (leave) 処理
クライアントが {"event":"leave","room":"general"} のようなイベントを送信した場合、そのクライアントを該当ルームから削除する
クライアントが明示的に leave を送らず、ソケットが切断された場合でも、後片付けとして参加中の全ルームからクライアントを削除しておく

4. メッセージ配信 (broadcast) 処理
クライアントが {"event":"broadcast","room":"general","message":"Hello"} のようなイベントを送信した場合、該当ルームに参加している全てのクライアントへ "Hello" のメッセージを送信する

これで、基本的な「参加・退出・メッセージ配信」のサイクルが完成しました。
ちゃんと動くか確認してみましょう。
動作確認
1. サーバー起動

2. wscat で接続

3. ルームに参加

コンソールに 127.0.0.1:XXXXX joined room general と表示されれば OK。
4. もう 1 つのコンソールで同じ手順を繰り返し、同じルームに参加

5. ルームにメッセージを配信

2 つのコンソールに Hello! と表示されれば OK。
6. ルームから退出

再度メッセージを配信しても、退出したクライアントのコンソールに届かなければ OK。


GOOD 👍️
まとめ
本記事では、Rust でリアルタイムチャットサーバーを実装する手順を紹介しました。
今回紹介したのは、あくまでも「最低限の仕組み」ですが、Rust を用いたマルチスレッドや非同期処理の概要を掴むには役立ったかなと感じています。
実運用を考えると、モニタリングやスケーリング、セキュリティなど、さまざまな課題が待ち受けています。
でもまぁ、Rust は書いてて楽しいので頑張れそうですw
もし、この記事が Rust でのリアルタイム通信の基本構造を理解する一助になれば幸いです。
ありがとうございました!