ホーム › ネットワーク › N-03: TCP ソケット通信
N-03: TCP ソケット通信(ServerSocket / Socket)
ServerSocket と Socket を使った TCP ソケット通信の基本を解説します。TCP は接続確立(3ウェイハンドシェイク)・データ転送・切断という 手順を踏む信頼性の高いプロトコルです。HTTP や FTP など多くのアプリケーション層プロトコルの基盤となっています。
TCP 通信の仕組みと主なクラス
| クラス | 役割 | 主なメソッド |
|---|---|---|
| ServerSocket | サーバー側。指定ポートで待受し、クライアントの接続を受け付ける | accept()、close() |
| Socket | クライアント側。サーバーへの接続と双方向通信を担う | getInputStream()、getOutputStream()、close() |
| BufferedReader | テキスト行単位で読み込むラッパー | readLine() |
| PrintWriter | テキスト行単位で書き込むラッパー | println() |
サンプルコード
シンプルなエコーサーバーの実装例です。サーバーはクライアントから受信したメッセージに 「ECHO: 」を付けて返します。「EXIT」を受信するとサーバーを終了します。
よくあるミス・注意点
⚠️ ソケットは必ず try-with-resources でクローズする
Socket と ServerSocket は Closeable を実装しているため、 try-with-resources で自動的にクローズできます。 クローズを忘れると OS のファイルディスクリプタ(FD)が枯渇し、 新しい接続を受け付けられなくなります。
// NG: クローズ漏れでリソースが枯渇する可能性がある
Socket socket = new Socket("localhost", 9000);
// ... 処理 ...
// close() を呼び忘れると FD リークが起きる
// OK: try-with-resources で確実にクローズする
try (Socket socket = new Socket("localhost", 9000)) {
// ... 処理 ...
} // ブロックを抜けると自動的に socket.close() が呼ばれる📌 accept() はブロッキング呼び出しなので別スレッドで実行する
serverSocket.accept() はクライアントが接続してくるまで 処理をブロックします。メインスレッドで呼び出すとアプリケーション全体が停止してしまうため、 必ず別スレッド(または ExecutorService)で実行しましょう。
📌 ポート番号は 1024 以上を使う
0〜1023 番ポート(ウェルノウンポート)は OS の管理下にあり、 バインドには管理者権限(root / Administrator)が必要です。 アプリケーションでは 1024〜65535 の範囲のポートを使いましょう。 他のアプリケーションと競合しないよう、使用前に空きポートを確認してください。
📌 Java 21 の仮想スレッドで大量クライアントを効率的に処理できる
従来の OS スレッドは生成コストが高く、数千クライアントの同時接続で スレッド数が増えすぎてメモリ不足になりがちです。 Java 21 の仮想スレッド(Executors.newVirtualThreadPerTaskExecutor())は 軽量で大量生成できるため、1 クライアント 1 スレッドのシンプルな設計を維持しながら 高いスループットを実現できます。
テストする観点
- サーバーとクライアントが正常に接続でき、メッセージの送受信が行えるか
- 送信したメッセージに「ECHO: 」が付いて正しく返ってくるか
- 「EXIT」メッセージを送るとサーバーが正常終了するか
- 接続を切断した際に
readLine()がnullを返してループが終了するか - サーバーが起動していない状態でクライアントが接続しようとすると
ConnectExceptionがスローされるか - ソケットのタイムアウト(
setSoTimeout())を設定した場合にSocketTimeoutExceptionが発生するか