Ruby で IPv4 IPv6 に両対応した TCP サーバの書き方 select を使わない篇
echo サーバのサンプルです
Ruby レシピブック第 2 版には select を使うコードがありますが、OS に、RFC 3493 の§3.7 で示されている IPv4 マッピングの機能があってそれを利用できれば、ひとつのソケットで両方をハンドルできます
システムのデフォルトで IPV6_V6ONLY がオフであれば明示的に操作する必要はないのですが、デフォルトがオンのシステムがけっこうあるので、明示的にオフにしています(そのために Socket オブジェクトを使わざるをえません)
ruby 1.8 の場合、IPPROTO_IPV6 と IPV6_V6ONLY が Ruby の定数になっていないので直接ハードコーディングします
require "socket" include Socket::Constants port = 12345 server_socket = Socket.new PF_INET6, SOCK_STREAM, 0 server_socket.setsockopt 41, 27, false # 41 = IPPROTO_IPV6, 27 = IPV6_V6ONLY server_socket.bind Socket.pack_sockaddr_in port, "::" server_socket.listen 5 loop { Thread.fork(server_socket.accept){|sock_addr| sock = sock_addr[0] addr = sock_addr[1] print "client = " p Socket.unpack_sockaddr_in addr begin while buf = sock.gets do sock.write buf sock.flush end ensure unless sock.closed? then sock.close end end } }
以下は ruby 1.9 用。定数 IPPROTO_IPV6 と IPV6_V6ONLY が使えます
require "socket" include Socket::Constants port = 12345 server_socket = Socket.new PF_INET6, SOCK_STREAM, 0 server_socket.setsockopt IPPROTO_IPV6, IPV6_V6ONLY, false server_socket.bind Socket.pack_sockaddr_in port, "::" server_socket.listen 5 loop { Thread.fork(server_socket.accept){|sock_addr| sock = sock_addr[0] addr = sock_addr[1] print "client = " p Socket.unpack_sockaddr_in addr begin while buf = sock.gets do sock.write buf sock.flush end ensure unless sock.closed? then sock.close end end } }