はじめに: java.netパッケージとは?
java.net
パッケージは、Javaプラットフォームにおいてネットワーク関連の機能を提供する中心的なライブラリです。Javaの初期バージョンから存在し、アプリケーションがインターネットや他のネットワークと対話するための基本的なクラス群を提供します。このパッケージを利用することで、開発者は低レベルのソケット通信から高レベルのHTTPリクエストまで、多岐にわたるネットワークプログラミングを実装できます。
具体的には、以下のような機能が含まれています。
- リソースへのアクセス: URLを指定してWebサーバーなどのリソースにアクセスする機能。
- HTTP通信: Web APIの呼び出しやWebページの取得など、HTTPプロトコルに基づいた通信。
- TCP/IPソケット通信: 信頼性の高いコネクション指向の通信を実現する機能。クライアントとサーバー間での確実なデータ交換に利用されます。
- UDPソケット通信: 高速だが信頼性の保証がないコネクションレス型の通信。ストリーミングやオンラインゲームなどで利用されます。
- 名前解決: ホスト名(例:
www.google.com
)とIPアドレス(例:142.250.196.196
)を相互に変換する機能。
この記事では、これらの主要な機能について、基本的な概念から具体的なコード例までを交えながら、詳細に解説していきます。特に、Javaのバージョンアップに伴う変化、例えばJava 11で導入された新しいHTTPクライアントなど、現代的な開発において知っておくべき重要なポイントにも焦点を当てます。
第1章: URLとURI – リソースの識別子
ネットワーク上のリソース(Webページ、画像、APIエンドポイントなど)を扱う上で基本となるのが、その場所を示す識別子です。java.net
パッケージでは、そのためにURI
とURL
という2つのクラスが提供されています。
URIとURLの違い
この2つのクラスは似ていますが、明確な役割の違いがあります。
クラス | 役割 | 特徴 |
---|---|---|
java.net.URI | リソースの識別子 (Identifier) | リソースを識別するための構文的な表現を扱います。 「どのような形式でリソースが表現されるべきか」というルールに従っているかを検証しますが、そのリソースが実際に存在するか、アクセス可能かは問いません。 相対パス(例: /path/to/file )も扱えます。 |
java.net.URL | リソースのロケータ (Locator) | リソースの場所に加えて、それを取得するためのアクセス機構(プロトコル)を含みます。URL オブジェクトは、そのリソースへの接続を開く(openConnection() )能力を持ちます。そのため、サポートされているプロトコル(http, https, ftpなど)が必要であり、リソースの場所を特定できなければなりません。 |
簡単に言えば、URI
は「住所の書き方のルール」、URL
は「実際に手紙が届く住所」のような関係です。すべてのURL
はURI
の構文に従っていますが、すべてのURI
がURL
であるとは限りません。
URLクラスの基本的な使い方と注意点
URL
オブジェクトは、特定のリソースへの接続を作成するための出発点となります。
【重要】コンストラクタの非推奨化
かつては new URL("https://www.example.com")
のようにコンストラクタで直接インスタンスを生成するのが一般的でした。しかし、このコンストラクタは、プロトコルの検証が不十分であるなどの問題から、Java 20 (2023年3月リリース) で非推奨となりました。
現在推奨されている方法は、まず構文的に安全なURI
オブジェクトを作成し、それをURL
に変換する方法です。
<?xml version="1.0" encoding="UTF-8"?>
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
public class UrlExample { public static void main(String[] args) { String urlString = "https://www.example.com:8080/path/to/resource?key=value#fragment"; try { // 推奨される方法: URI.create() または new URI() を経由する URI uri = new URI(urlString); URL url = uri.toURL(); System.out.println("URL: " + url); System.out.println("Protocol: " + url.getProtocol()); // https System.out.println("Host: " + url.getHost()); // www.example.com System.out.println("Port: " + url.getPort()); // 8080 System.out.println("Path: " + url.getPath()); // /path/to/resource System.out.println("Query: " + url.getQuery()); // key=value System.out.println("Fragment: " + url.getRef()); // fragment (RefはReferenceの意) } catch (URISyntaxException e) { System.err.println("URIの構文が正しくありません: " + e.getMessage()); } catch (MalformedURLException e) { System.err.println("不正な形式のURLです: " + e.getMessage()); } catch (IllegalArgumentException e) { System.err.println("不正な引数です: " + e.getMessage()); } }
}
この方法は、まずURI
クラスが文字列の構文を厳密にチェックしてくれるため、より安全で堅牢なコードになります。常にURI
を経由する方法を習慣づけるようにしましょう。
第2章: HTTP通信の実践(1) – 伝統的な HttpURLConnection
java.net.HttpURLConnection
は、Javaの初期から提供されている、HTTP通信を行うための基本的なクラスです。Java 11でより高機能なHttpClient
が登場しましたが、HttpURLConnection
は外部ライブラリを必要とせず、標準APIのみで手軽に利用できるため、依然として多くの場面で使われています。
HttpURLConnection
は抽象クラスであり、実際のインスタンスはURL.openConnection()
メソッドが返すオブジェクトをキャストして取得します。
GETリクエストの送信
GETリクエストは、Webサーバーから情報を取得するための最も一般的なメソッドです。
<?xml version="1.0" encoding="UTF-8"?>
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
public class HttpUrlConnectionGetExample { public static void main(String[] args) { HttpURLConnection connection = null; try { URL url = new URI("https://api.github.com/users/google").toURL(); connection = (HttpURLConnection) url.openConnection(); // 1. リクエストメソッドの設定 (GETはデフォルトなので省略可能) connection.setRequestMethod("GET"); // 2. タイムアウト設定 (ミリ秒) connection.setConnectTimeout(5000); // 接続タイムアウト connection.setReadTimeout(5000); // 読み取りタイムアウト // 3. レスポンスコードの確認 int responseCode = connection.getResponseCode(); System.out.println("Response Code: " + responseCode); if (responseCode == HttpURLConnection.HTTP_OK) { // 200 OK // 4. レスポンスボディの読み取り try (BufferedReader in = new BufferedReader( new InputStreamReader(connection.getInputStream()))) { String inputLine; StringBuilder content = new StringBuilder(); while ((inputLine = in.readLine()) != null) { content.append(inputLine); } System.out.println("Response Body: " + content.toString().substring(0, 200) + "..."); } } else { System.out.println("GET request not worked"); } } catch (Exception e) { e.printStackTrace(); } finally { if (connection != null) { // 5. 接続の解放 connection.disconnect(); } } }
}
コードのポイント
url.openConnection()
でURLConnection
を取得し、HttpURLConnection
にキャストします。setRequestMethod()
でHTTPメソッドを指定します。- タイムアウト設定は非常に重要です。設定しない場合、ネットワークの問題で応答がないサーバーに対して、プログラムが無限に待ち続けてしまう可能性があります。
getResponseCode()
でHTTPステータスコード(200, 404, 500など)を取得します。- レスポンスボディは
getInputStream()
で取得したInputStream
から読み取ります。文字化けを防ぐため、InputStreamReader
で文字コードを指定することが推奨されます。 - 最後に
finally
ブロックでdisconnect()
を呼び出し、リソースを確実に解放します。
POSTリクエストの送信
POSTリクエストは、サーバーにデータを送信して新しいリソースを作成したり、既存のデータを更新したりするために使用されます。
<?xml version="1.0" encoding="UTF-8"?>
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class HttpUrlConnectionPostExample { public static void main(String[] args) { HttpURLConnection connection = null; try { URL url = new URI("https://jsonplaceholder.typicode.com/posts").toURL(); connection = (HttpURLConnection) url.openConnection(); // 1. POSTメソッドの設定 connection.setRequestMethod("POST"); // 2. ヘッダーの設定 connection.setRequestProperty("Content-Type", "application/json; utf-8"); connection.setRequestProperty("Accept", "application/json"); // 3. リクエストボディの送信を有効にする connection.setDoOutput(true); // 4. 送信するJSONデータ String jsonInputString = "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}"; // 5. リクエストボディの書き込み try(OutputStream os = connection.getOutputStream()) { byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } // 6. レスポンスの読み取り (GETと同様) int responseCode = connection.getResponseCode(); System.out.println("Response Code: " + responseCode); try(BufferedReader br = new BufferedReader( new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder response = new StringBuilder(); String responseLine; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } System.out.println("Response Body: " + response.toString()); } } catch (Exception e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } } }
}
POSTリクエストのポイント
setRequestMethod("POST")
でメソッドをPOSTに設定します。setRequestProperty()
でContent-Type
などのリクエストヘッダーを設定します。JSONデータを送信する場合はapplication/json
を指定します。connection.setDoOutput(true);
が必須です。これをtrue
にすることで、リクエストボディの送信が許可されます。getOutputStream()
で取得したOutputStream
に、送信したいデータを書き込みます。この際、文字コードを明示的に指定することが重要です(例:StandardCharsets.UTF_8
)。
第3章: HTTP通信の実践(2) – モダンな HttpClient (Java 11+)
Java 11 (2018年9月リリース) で、新しい標準HTTPクライアントAPI (java.net.http.HttpClient
) が正式に導入されました。これは、HttpURLConnection
が抱えていたいくつかの課題を解決し、より現代的なアプリケーション開発に適した機能を提供します。
HttpURLConnectionとの比較
HttpClient
がHttpURLConnection
に比べて優れている点は以下の通りです。
機能 | HttpURLConnection | HttpClient (Java 11+) |
---|---|---|
APIの設計 | 古く、直感的でない部分がある | モダンで流れるような (Fluent) API。ビルダーパターンを採用し、不変オブジェクトを生成する。 |
通信モデル | ブロッキングI/Oが基本 | 同期的 (ブロッキング) と 非同期的 (ノンブロッキング) の両方をネイティブでサポート。 |
HTTPバージョン | HTTP/1.1 | HTTP/1.1 と HTTP/2 をサポート。サーバーが対応していれば自動でHTTP/2を使用する。 |
その他プロトコル | なし | WebSocket をサポート。 |
特別な理由がない限り、新規にJava 11以上で開発を行う場合は、HttpClient
の使用が強く推奨されます。
同期的なGETリクエスト
HttpClient
を使ったAPI呼び出しは、リクエストの作成、クライアントによる送信、という明確なステップに分かれています。
<?xml version="1.0" encoding="UTF-8"?>
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class HttpClientSyncExample { public static void main(String[] args) { // 1. HttpClientの作成 (再利用可能) HttpClient client = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_2) .connectTimeout(Duration.ofSeconds(10)) .build(); // 2. HttpRequestの作成 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.github.com/users/google")) .header("Accept", "application/vnd.github.v3+json") .GET() // メソッド指定 (デフォルトはGET) .build(); try { // 3. リクエストの送信とレスポンスの受信 // BodyHandlersはレスポンスボディをどう処理するかを指定する HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); // 4. レスポンス情報の取得 System.out.println("Status Code: " + response.statusCode()); System.out.println("Headers: " + response.headers()); System.out.println("Body: " + response.body().substring(0, 200) + "..."); } catch (Exception e) { e.printStackTrace(); } }
}
非同期的なPOSTリクエスト
HttpClient
の真価は非同期処理にあります。sendAsync()
メソッドはCompletableFuture
を返し、UIスレッドをブロックすることなくネットワーク通信を行えます。
<?xml version="1.0" encoding="UTF-8"?>
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class HttpClientAsyncPostExample { public static void main(String[] args) throws ExecutionException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); String json = "{\"title\": \"foo_async\", \"body\": \"bar_async\", \"userId\": 2}"; HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://jsonplaceholder.typicode.com/posts")) .header("Content-Type", "application/json") // BodyPublishersはリクエストボディをどう提供するかを指定する .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); // 1. 非同期でリクエストを送信 CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()); // 2. レスポンスが返ってきた後の処理を定義 (ラムダ式) futureResponse.thenApply(HttpResponse::body) .thenAccept(System.out::println); // 3. (デモのため) 処理が終わるまで待機 futureResponse.join(); }
}
HttpClientのポイント
HttpClient
インスタンスは、設定が同じであれば複数のリクエストで再利用することが推奨されます。スレッドセーフです。- リクエストとレスポンスの本体(ボディ)は、それぞれ
HttpRequest.BodyPublisher
とHttpResponse.BodyHandler
によって柔軟に処理方法を指定できます。文字列、バイト配列、ファイルなど、様々な形式に対応しています。 - 非同期処理は、GUIアプリケーションや、多数のAPIを並行して呼び出す高性能なバックエンドサービスなどで特に強力な武器となります。
第4章: TCPソケットプログラミング – Socket と ServerSocket
HTTPのようなアプリケーション層のプロトコルより低いレベル、トランスポート層での通信を直接扱いたい場合、ソケットプログラミングが必要になります。java.net
パッケージは、TCPプロトコルによる通信のためのSocket
とServerSocket
クラスを提供します。
TCPはコネクション指向のプロトコルです。通信を始める前に、クライアントとサーバーの間で「接続」を確立します。これにより、送信したデータが送信した順序で、欠けることなく相手に届くことが保証され、信頼性の高い通信が実現されます。
ServerSocket
: サーバー側で使用します。特定のポート番号でクライアントからの接続要求を待ち受けます(listen)。Socket
: サーバーとクライアントの両方で使用されます。クライアント側ではサーバーに接続するために生成し、サーバー側ではServerSocket
が接続要求を受け入れた(accept)結果として生成されます。実際のデータ交換は、このSocket
の入出力ストリームを介して行われます。
シンプルなエコーサーバーの実装例
クライアントから送られてきたメッセージを、そのままクライアントに送り返す「エコーサーバー」と、そのクライアントの例を見てみましょう。
サーバー側コード (EchoServer.java)
<?xml version="1.0" encoding="UTF-8"?>
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer { public static void main(String[] args) { int port = 12345; System.out.println("サーバーがポート " + port + " で起動しました。"); try (ServerSocket serverSocket = new ServerSocket(port)) { System.out.println("クライアントからの接続を待っています..."); // accept()はクライアントが接続してくるまで処理をブロックする try (Socket clientSocket = serverSocket.accept(); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) { System.out.println("クライアントが接続しました: " + clientSocket.getRemoteSocketAddress()); out.println("ようこそ!メッセージを送ってください。'exit'で終了します。"); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println("クライアントからのメッセージ: " + inputLine); if ("exit".equalsIgnoreCase(inputLine)) { out.println("さようなら。"); break; } // エコーバック out.println("Echo: " + inputLine); } } System.out.println("クライアントとの接続を終了しました。"); } catch (Exception e) { e.printStackTrace(); } }
}
クライアント側コード (EchoClient.java)
<?xml version="1.0" encoding="UTF-8"?>
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class EchoClient { public static void main(String[] args) { String hostname = "localhost"; int port = 12345; try (Socket socket = new Socket(hostname, port); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) { System.out.println("サーバーに接続しました。"); // サーバーからの最初のメッセージを受信 System.out.println("サーバー: " + in.readLine()); String userInput; while ((userInput = stdIn.readLine()) != null) { // ユーザーが入力したメッセージをサーバーに送信 out.println(userInput); // サーバーからのエコーメッセージを受信して表示 String serverResponse = in.readLine(); System.out.println("サーバー: " + serverResponse); if ("さようなら。".equals(serverResponse)) { break; } } } catch (Exception e) { System.err.println(hostname + ":" + port + "への接続に失敗しました。"); e.printStackTrace(); } }
}
ソケットプログラミングのポイント
try-with-resources
文の活用:Socket
やServerSocket
、関連するストリームは、使用後に必ず閉じる必要があるリソースです。try-with-resources
文を使うことで、ブロックを抜ける際に自動的にclose()
メソッドが呼び出され、リソースリークを防ぐことができます。- ブロッキング動作:
serverSocket.accept()
やストリームのread()
メソッドは、処理が可能になるまで現在のスレッドをブロックします。そのため、複数のクライアントを同時に処理するには、クライアントごとに新しいスレッドを作成するマルチスレッド設計が必要になります。 - 入出力ストリーム:
Socket
からgetInputStream()
とgetOutputStream()
を取得し、データの読み書きを行います。テキストデータを扱う場合はBufferedReader
やPrintWriter
でラップすると便利です。
第5章: UDPソケットプログラミング – DatagramSocket と DatagramPacket
TCPとは対照的に、UDP (User Datagram Protocol) はコネクションレスのプロトコルです。事前に接続を確立せず、一方的にデータをパケット(データグラム)として送りつけます。
この方式には以下のような特徴があります。
- 高速性: 接続・切断のオーバーヘッドがないため、通信が高速です。
- 信頼性の欠如: 送信したパケットが相手に届く保証はありません。また、パケットが送信した順序通りに届くとも限りませんし、同じパケットが複数回届く可能性もあります。
そのため、データの信頼性よりも速度やリアルタイム性が重視される用途、例えばDNSの名前解決、DHCP、オンラインゲームのプレイヤー位置情報、ビデオや音声のストリーミングなどに利用されます。
Javaでは、UDP通信のために以下のクラスを使用します。
DatagramSocket
: UDPパケットを送受信するためのソケット(通信の端点)。DatagramPacket
: 送受信されるデータそのものと、宛先または送信元のIPアドレス・ポート番号を格納するコンテナ。
シンプルなUDP送受信の実装例
受信側コード (UdpReceiver.java)
<?xml version="1.0" encoding="UTF-8"?>
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpReceiver { public static void main(String[] args) { int port = 54321; System.out.println("UDPレシーバーがポート " + port + " で待機中..."); try (DatagramSocket socket = new DatagramSocket(port)) { byte[] buffer = new byte; // 受信用のバッファ while (true) { // 1. 受信用のDatagramPacketを作成 DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // 2. パケットを受信するまでブロック socket.receive(packet); // 3. 受信したデータを文字列に変換 String receivedMessage = new String(packet.getData(), 0, packet.getLength()); InetAddress senderAddress = packet.getAddress(); int senderPort = packet.getPort(); System.out.println("受信: [" + senderAddress.getHostAddress() + ":" + senderPort + "] " + receivedMessage); if ("exit".equalsIgnoreCase(receivedMessage)) { System.out.println("終了コマンドを受信しました。"); break; } } } catch (Exception e) { e.printStackTrace(); } }
}
送信側コード (UdpSender.java)
<?xml version="1.0" encoding="UTF-8"?>
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UdpSender { public static void main(String[] args) { String hostname = "localhost"; int port = 54321; try (DatagramSocket socket = new DatagramSocket(); // 送信側はポート指定不要 Scanner scanner = new Scanner(System.in)) { InetAddress address = InetAddress.getByName(hostname); System.out.println("送信するメッセージを入力してください ('exit'で終了):"); while (scanner.hasNextLine()) { String message = scanner.nextLine(); byte[] buffer = message.getBytes(); // 1. 送信用のDatagramPacketを作成 (データ、長さ、宛先アドレス、宛先ポート) DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, port); // 2. パケットを送信 socket.send(packet); System.out.println("送信: " + message); if ("exit".equalsIgnoreCase(message)) { break; } } } catch (Exception e) { e.printStackTrace(); } }
}
UDPプログラミングのポイント
- データはパケット単位: 通信は常に
DatagramPacket
という単位で行われます。大きなデータを送る場合は、自分で分割・再構築する仕組みが必要です。 - 受信バッファ: 受信側は、受け取るパケットの最大サイズを想定して、十分な大きさのバイト配列(バッファ)を事前に用意しておく必要があります。
- 信頼性の確保: UDP自体には信頼性がないため、もし「データが確実に届いたか確認したい」「失われたパケットを再送したい」といった要件がある場合は、アプリケーション層で独自の確認応答(ACK)や再送制御のロジックを実装する必要があります。
第6章: ホスト名とIPアドレス – InetAddress
ネットワークプログラミングにおいて、通信相手を特定するためにはIPアドレスが必要ですが、人間にとってはwww.google.com
のようなホスト名の方がはるかに覚えやすく、扱いやすいです。このホスト名とIPアドレスの対応付けを管理しているのがDNS (Domain Name System) であり、ホスト名からIPアドレスを調べることを名前解決と呼びます。
Javaのjava.net.InetAddress
クラスは、この名前解決を行うための機能を提供します。このクラスのインスタンスはIPアドレスを表しますが、ホスト名に関する情報も保持することができます。
InetAddress
にはコンストラクタがなく、静的ファクトリメソッドを使ってインスタンスを取得します。
主な使い方
<?xml version="1.0" encoding="UTF-8"?>
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
public class InetAddressExample { public static void main(String[] args) { try { // 1. ホスト名からInetAddressインスタンスを取得 // DNSに問い合わせが行われる InetAddress googleAddress = InetAddress.getByName("www.google.com"); System.out.println("--- www.google.com の情報 ---"); System.out.println("Host Name: " + googleAddress.getHostName()); System.out.println("IP Address: " + googleAddress.getHostAddress()); System.out.println(); // 2. 1つのホスト名に複数のIPアドレスが関連付けられている場合 InetAddress[] allGoogleAddresses = InetAddress.getAllByName("www.google.com"); System.out.println("--- www.google.com の全てのIPアドレス ---"); // Arrays.stream(allGoogleAddresses).forEach(System.out::println); とも書ける for (InetAddress addr : allGoogleAddresses) { System.out.println(addr.getHostAddress()); } System.out.println(); // 3. 自身のローカルホスト情報を取得 InetAddress localHost = InetAddress.getLocalHost(); System.out.println("--- ローカルホストの情報 ---"); System.out.println("Host Name: " + localHost.getHostName()); System.out.println("IP Address: " + localHost.getHostAddress()); System.out.println(); // 4. IPアドレス文字列からInetAddressインスタンスを取得 // この場合、逆引きが行われない限りホスト名はIPアドレスのまま InetAddress specificAddress = InetAddress.getByName("8.8.8.8"); System.out.println("--- 8.8.8.8 の情報 ---"); System.out.println("Host Name (逆引き試行後): " + specificAddress.getHostName()); System.out.println("IP Address: " + specificAddress.getHostAddress()); } catch (UnknownHostException e) { System.err.println("ホストが見つかりません: " + e.getMessage()); } }
}
InetAddressのポイント
getByName()
やgetAllByName()
は、内部でネットワークアクセス(DNSクエリ)を発生させます。そのため、ネットワークに接続されていない環境や、DNSサーバーに問題がある場合にはUnknownHostException
がスローされる可能性があります。- パフォーマンス上の理由から、Javaは一度解決した名前とIPアドレスの対応を内部的にキャッシュします。このキャッシュの挙動は、セキュリティプロパティ(
networkaddress.cache.ttl
など)で調整できます。 InetAddress
はIPv4 (Inet4Address
) とIPv6 (Inet6Address
) の両方をサポートしています。
まとめ
本記事では、Javaのネットワークプログラミングの根幹をなすjava.net
パッケージについて、その主要なクラスと機能を紹介しました。
高レベルなHTTP通信から見ていくと、
- URL/URI: ネットワーク上のリソースを特定するための基本。Java 20以降は
URI
経由でURL
を生成する方法が推奨されます。 - HttpURLConnection: 伝統的でシンプルなHTTPクライアント。標準ライブラリのみで動作する手軽さがあります。
- HttpClient (Java 11+): HTTP/2や非同期処理をサポートするモダンで高機能なクライアント。新規開発ではこちらの利用が第一候補となります。
より低レベルな通信では、
- Socket/ServerSocket: 信頼性の高いTCP通信を実現するための基本。コネクションを確立し、ストリームベースでデータをやり取りします。
- DatagramSocket/DatagramPacket: 高速なUDP通信を実現します。パケット単位でデータを送受信し、信頼性はアプリケーション側で担保する必要があります。
- InetAddress: ホスト名とIPアドレスの相互変換を行い、通信相手を特定する上で不可欠な機能です。
java.net
パッケージを使いこなすことで、単純なAPIクライアントから、複雑なP2Pアプリケーション、高性能なネットワークサーバーまで、Javaで実現できることの幅は大きく広がります。それぞれのクラスの特性を理解し、目的に応じて適切なツールを選択することが、効果的なネットワークプログラミングへの第一歩となるでしょう。