WindowsでTCPSocket#peeraddrに時間がかかる

おそらくFAQだと思うのですが私の状況にぴったりな情報が無かったので書きます。
Rubyでnet/ftpを使用したNet::FTP#getbinaryfileが遅いという事で調べたところWindows環境でIPからアドレスの逆引きができない場合にTCPSocket#peeraddrに時間がかかるという問題があることがわかりました。
この現象の再現コードは以下のような物です。

require 'benchmark' 
require 'socket'

Benchmark.bm do |rep| 
  rep.report do 
    socket = TCPSocket.open('192.168.100.1', '80')
    p socket.peeraddr
  end
end

実行結果が

user system total real
["AF_INET", 80, "192.168.100.1", "192.168.100.1"]
0.000000 0.000000 0.000000 ( 4.543414)

この様に4.5秒程度かかる場合にはこの問題があります。
この問題を解消するためには以下の方法があります。

  • アドレスの逆引きをできるように設定する

例えばsystem32\drivers\etc\hostsに設定に

192.168.100.1 hostname

この様に設定します。

  • 逆引きを行わないようにする

BasicSocketのデフォルトでは以下のメソッドでアドレスの逆引きが行われます。

  • BasicSocket#recv
  • IPSocket#recvfrom
  • UNIXSocket#recvfrom
  • Socket#recvfrom
  • IPSocket#addr
  • IPSocket#peeraddr
  • Socket.getaddrinfo

このデフォルトを変更してアドレスの逆引きを行わないようにするには
TCPSocket.do_not_reverse_lookupをtrueにします。

require 'benchmark' 
require 'socket'

TCPSocket.do_not_reverse_lookup = true

Benchmark.bm do |rep| 
  rep.report do 
    socket = TCPSocket.open('192.168.100.1', '80')
    p socket.peeraddr
  end
end

(参考)Ruby 1.8.7 リファレンスマニュアル > ライブラリ一覧 > socketライブラリ > BasicSocketクラス > do_not_reverse_lookup
http://doc.okkez.net/187/view/method/BasicSocket/s/do_not_reverse_lookup
このTCPSocket.do_not_reverse_lookupの設定はグローバルに影響します。
この対応で

user system total real
["AF_INET", 80, "192.168.100.1", "192.168.100.1"]
0.000000 0.016000 0.016000 ( 0.015600)

この様に数十ミリ秒になります。


この問題の影響を受けるのは上記のとおりの各メソッドですがこれらを利用するFTPなどにも影響があるので注意が必要です。

(参考にしたページ)

  • [ruby-list:46666] TCPSocket#peeraddr に時間がかかる。

http://old.nabble.com/-ruby-list%3A46666--TCPSocket-peeraddr--%E3%81%AB%E6%99%82%E9%96%93%E3%81%8C%E3%81%8B%E3%81%8B%E3%82%8B%E3%80%82-to26855153.html#a26855153

  • peeraddr が遅い?

http://sonic64.com/2004-02-03.html#2004-02-03-101

  • 続「peeraddr が遅い?」 NetBIOS が悪い?

http://sonic64.com/2004-02-04.html#2004-02-04-1