RubyでTCPサーバー/クライアントを作ってみる
サンプルをコピペしただけだと上手く動かないor微妙な点がいくつかあったので備忘録です。
サーバー
# server
require 'socket'
# 3030番ポートを開く
gs = TCPServer.open(nil, 3030)
socks = [gs]
puts "server is on #{gs.addr}"
loop {
nsock = select(socks)
next unless nsock[0]
nsock[0].each do |s|
if s == gs
ns = s.accept
socks.unshift ns
puts "#{ns} is accept"
else
if s.eof?
puts "#{s} is gone."
s.close
socks.delete(s)
else
msg = s.readpartial(4 * 1024)
p msg
end
break
end
end
}
クライアント
# client
require 'socket'
s = TCPSocket.open("localhost", 3030)
while msg = gets
puts "write #{msg}"
s.write msg
end
仕様を変更した点
- サーバー側の通信読み込み部分を
s.gets
からs.readpartial
へ変更
文字列ならばいいのかもしれないが、今後バイト列を書き込み/読み込みを考えるとgets
ではダメな可能性がある。 - サーバー側の
accept
時にsocks.push
ではなくsocks.unshift
を用いTCPServer
ソケットの優先度を下げた - select後TCPSocketに対して通信を行ったあとはbreakで再度
select
へ移行するようにした。
上2つはwindows特有のバグなのかは分かりませんが、readpartial
時に指定した以上の長さの通信がある場合
次回のselect
で、どういうわけかTCPServer
が読み込み待ちになるバグへの対処です。
反省
s.nread
でブロックなしに読み取れるバイト数が取れるのでコレを使うといいかもしれない。
但し、タイミング次第でやはりselectのバグ?を踏むと思われるので待ち行列の優先度等については考慮する必要がある。