torutkのブログ

ソフトウェア・エンジニアのブログ

NIOパッケージのServerSocketChannelはWindows上ではIPv6をlistenしない

Windows OSでNIOパッケージを使ってTCPサーバを記述したときにIPv6が使えないという問題にあたりました。BugIDにも登録されてますが、解決のめどはないようです。

java.io.ServerSocketでTCPサーバを記述したときは、IPv4IPv6のどちらもlistenしており、クライアント側からはIPv4IPv6のどちらを使ってもこのTCPサーバと接続できました。

問題の確認

確認環境

Windows Vista(64bit)、JDK 6 Update4

確認コード(java.nioパッケージ使用)
        int port = 20002;
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.socket().bind(new InetSocketAddress(port));
        SocketChannel peer = serverChannel.accept();

このとき、netstatコマンド(-aオプション付き)を実行すると、

  プロトコル  ローカル アドレス          外部アドレス        状態
  TCP    0.0.0.0:20002          keisuke:0              LISTENING

IPv4のみlistenしていることがわかります。

確認コード(java.ioパッケージ使用)
        int port = 12345;
        ServerSocket serverSocket = new ServerSocket(port);
        Socket connection = serverSocket.accept();

このとき、netstatコマンドの結果は、

  プロトコル  ローカル アドレス          外部アドレス        状態
  TCP    0.0.0.0:12345          keisuke:0              LISTENING
  TCP    [::]:12345             keisuke:0              LISTENING

と、IPv4IPv6の双方をlistenしていることがわかります。

回避の試み

TCPサーバのbind時に明示的に"::"を指定する
Exception in thread "main" java.net.SocketException: Address family not supported by protocol family: bind
        at sun.nio.ch.Net.bind(Native Method)
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:119)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:59)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:52)   

Net.bindはnativeなので、JDK 6のb104(JDK 6 FCS版の1ヶ月ほど前のビルド)のソースを追ってみると、WinSockのbindを呼び出しでエラーになっており、エラーメッセージ"Address family not supported by protocol"はWSAGetLastError()の結果です。

Solaris10 x86上のJDK 6では

java.nioを使うTCPサーバのコードをSolaris 10 x86上でJDK 6で実行し、netstatでポートのlisten状況を調べてみると、IPv4IPv6ともにlistenしていました。

結論

Windows上でIPv6を使う可能性があるならば、当面java.nioパッケージは使わない。