目次
データのやり取り
- TCP接続は、ローカルソケットとリモートソケットをつなぐ管の連なりのようなイメージ
- 無数の機器がパケットをやり取りする、現実のネットワークのことは一旦忘れる
ストリーム
- TCPにはストリームのような性質がある
- ソケットはTCPを抽象化しているので、下のレイヤーのパケットのやり取りは気にする必要がない
- クライアントがデータを分割して送ったとしても、サーバはそれらを一塊にして受け取る
- データの境界は維持されないが、データの順番は維持される
データの読み取り
単純な読み取り
require 'socket'
Socket.tcp_server_loop(4481) do |connection|
puts connection.read
# データの読み取りが終わったら、ソケットを閉じることで
# クライアントの待ち状態を解除する
connection.close
end
- Rubyでは、
File
とソケット関係のクラスには、IO
という共通の親クラスがある
read
, write
, flush
メソッドなどが共通して利用できる
- read(2)やwrite(2)はシステムコールレベルで共通しているので、ファイル、ソケット、パイプ等で共通して利用できる
- 抽象化はOSレベルで提供されている
- UNIXにおいては、全てはファイルである
(物事は)それほど単純ではない
- 前述した方法には、クライアントが延々データを送りつけると、サーバが終了しなくなる問題がある
- この問題はEOF(end-of-file)によって発生している
- クライアントが送信終了の目印を送るまで、サーバは待ち続ける
読み取りの長さ
- 解決方法の1つは、読み取り時の最小の長さを定めること
read
メソッドに数値を渡すと読み取り時の最小の長さが設定される
- 下記コードの場合、1KBデータを読む度に画面上にデータが出力される
require 'socket'
one_kb = 1024 # バイト
Socket.tcp_server_loop(4481) do |connection|
# データを1KBずつ読む
while data = connection.read(one_kb)
puts data
end
connection.close
end
ブロッキングの性質
read
の呼び出しは、全てのデータが届くまで待ちを発生させる
- この問題の解決策は以下の2つ
- クライアントがEOFを送る
- サーバが部分的な読み取りを使用する
EOFイベント
read
の最中にEOFイベントを受け取ると、読み取りは中止される
- EOFは、特別な文字として紹介されることがある
- EOFは、文字というよりも、 状態を表すイベント である
- ソケットの書き込むデータが無くなると
shutdown
かclose
が呼び出され、EOFイベントが送信される
- EOFを送信するには、クライアントを以下のように実装する
require 'socket'
client = TCPSocket.new('localhost', 4481)
client.write('gekko')
client.close
- クライアントからEOFを送る際の最も簡単な方法は、ソケットを閉じることである
部分的な読み取り
readpartial
を実行すると、ブロックせず、すぐに利用できるデータだけが返る
readpartial
を実行する際には、読み取るデータの最大長を示す引数を渡す必要がある
require 'socket'
one_hundred_kb = 1024 * 100
Socket.tcp_server_loop(4481) do |connection|
begin
# データを100KB以下の固まりで読み取り
while data = connection.readpartial(one_hundred_kb) do
puts data
end
rescue EOFError
end
connection.close
end
readpartial
がEOFを受け取ると、EOFErrorが発生する点に注意が必要
本章で扱ったシステムコール
- read(2):
Socket#read
とSocket#readpartial
に対応