Working with TCP Sockets 読書メモ 第4章 データのやり取り

目次


データのやり取り

  • TCP接続は、ローカルソケットとリモートソケットをつなぐ管の連なりのようなイメージ
  • 無数の機器がパケットをやり取りする、現実のネットワークのことは一旦忘れる

ストリーム

  • TCPにはストリームのような性質がある
  • ソケットはTCPを抽象化しているので、下のレイヤーのパケットのやり取りは気にする必要がない
  • クライアントがデータを分割して送ったとしても、サーバはそれらを一塊にして受け取る
  • データの境界は維持されないが、データの順番は維持される

データの読み取り

単純な読み取り

  • 最も単純な方法はreadメソッドの使用である:
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つ
  1. クライアントがEOFを送る
  2. サーバが部分的な読み取りを使用する

EOFイベント

  • readの最中にEOFイベントを受け取ると、読み取りは中止される
  • EOFは、特別な文字として紹介されることがある
  • EOFは、文字というよりも、 状態を表すイベント である
  • ソケットの書き込むデータが無くなるとshutdowncloseが呼び出され、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#readSocket#readpartialに対応

コメントをどうぞ

コメントを残す