Ruby で IPv4 IPv6 に両対応した TCP サーバの書き方 select を使わない篇

echo サーバのサンプルです
Ruby レシピブック第 2 版には select を使うコードがありますが、OS に、RFC 3493 の§3.7 で示されている IPv4 マッピングの機能があってそれを利用できれば、ひとつのソケットで両方をハンドルできます
システムのデフォルトで IPV6_V6ONLY がオフであれば明示的に操作する必要はないのですが、デフォルトがオンのシステムがけっこうあるので、明示的にオフにしています(そのために Socket オブジェクトを使わざるをえません)
ruby 1.8 の場合、IPPROTO_IPV6IPV6_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_IPV6IPV6_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
	}
}