TCP Simultaneous Open
なんか完全に休暇モードになっちゃったので、P2P関連で調べもの。
NAT越えは一つの大きなテーマなのでその辺りを調べてて、TCP NAT Traversal(UDP Hole punchのTCP版みたいなもの)の論文
http://citeseer.ist.psu.edu/739668.html
を眺めていたら、TCP Simultaneous OpenというのがTCP仕様(RFC793)に有るのを知りました。
http://www.tcpipguide.com/free/t_TCPConnectionEstablishmentProcessTheThreeWayHandsh-4.htm
通常の3-way handshakeの例外事項みたいな仕様のようですが、検索してもあんまり出てこなかったので書いてみます。
通常のServer/ClientモデルのTCP接続との違い
普通TCPはサーバがbind(), listen(), accept()して、クライアントがconnect()する感じですが、これをクライアント(peerと言った方が正確かな?)同士で、同時にbind(), connect()するだけで、両者間のTCPセッションを確立させることが出来るようです。同時に、というのがポイントですが、これは何か仲介サーバとかを使って、せえの、、とすればそれほど難しくなさそうです。あと通常クライアントでは使わないbindを行うのは、相手側の送信元ポートにそのままconnectする形なので、相手側の送信元ポート番号が予測等が出来れば相手(と相手からみた自分も)bindする必要も無いと思います。
とりあえず実験
マシン2台(手元にあったMac2台)でまずはそもそもこの仕様が動くのか実験してみました。
rubyでちょっと書くと(普通のsocketの使い方サンプル+αですが・・)、ClientAは
#!/usr/bin/env ruby -wKU require 'socket' BIND_PORT = 8888 CONNECT_PORT = 7777 CONNECT_ADDR = '1.2.3.4' NAME = 'ClinetA' cnt = 0 begin s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) s.bind(Socket.sockaddr_in(BIND_PORT,"0.0.0.0")) s.connect(Socket.sockaddr_in(CONNECT_PORT, CONNECT_ADDR)) rescue Errno::ECONNREFUSED puts "#{NAME}: Errno::ECONNREFUSED" cnt += 1 s.close retry if (cnt < 100) exit(-1) end s.write "Hello from #{NAME}\n" print "#{NAME}: " + s.readline
ClientBは
#!/usr/bin/env ruby -wKU require 'socket' BIND_PORT = 7777 CONNECT_PORT = 8888 CONNECT_ADDR = '2.3.4.5' NAME = 'ClinetB' #以下clientAとおなじ
な感じです。これらClientA,Bを2台のマシンでほぼ同時に実行すると、数回"Errno::ECONNREFUSED"とか表示されますが、そのあとClientAでは、
ClinetA: Hello from ClinetB
ClientBでは
ClinetB: Hello from ClinetA
と表示されると思います。