読者です 読者をやめる 読者になる 読者になる

$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

Working With TCP Socketsを読んだ

tech book

Working with TCP Socketsを読みました。

Working With TCP Sockets (English Edition)

Working With TCP Sockets (English Edition)

これまでネットワークプログラミングの基礎みたいなことをあまり考えたことなかったので、結構勉強になりました。

次のようなことが勉強になりました。

  • serverやclientが通信を行うときにどのようなlifecycleをたどるのか
    • bind, listen, connectなど
  • 通信を効率的に行うために、kernelがどのようなことをするか
    • buffer層みたいなところで何をするか
    • サイズ指定のreadを実行した時にkernelはどのような領域を確保するか
  • 普通にnetcatとかlsofが便利とか
  • Network Architecture Patternsの基本形とか

基本的なことを簡単に解説するという感じだったので、webサーバとかnginxのproxyとか普通に使ってるけど、仕組みちゃんとは分かってないなーという感じの人は勉強になりそうでした。

以下はメモ書きです。

メモ書き

server lifecycle

  • create
  • bind : portを取得
  • listen : 接続待ちソケットとする、接続キューの最大長とかも指定する
  • accept : コネクション受付開始、1つくるまでブロッキング
  • close : コネクション閉じる、fdを閉じたり

netcatとかlsofとかちゃんと使ってなかったけど便利

client lifecycle

  • create, bind, connect, close

client側はephemeral rangeのportを勝手に使ってくれるのでbind呼ぶ必要ない

Exchanging Data

Sockets Can Read

  • closeかshutdown送ると結果的にEOFが送られる

Sockets Can Write

Buffering

  • writeが終わった時点ではkernelに渡ったことだけしか保証されていない
  • buffer層が良い感じにパフォーマンスを考慮して送ってくれている
  • なので基本的にはwriteに食わせるデータ量をどのくらいにすればいいか(多いか少ないか)気にしなくてもいい
  • すごい大きな量をbufferに入れるとメモリを圧迫するので気をつけるくらい
  • readで長さ指定するとkernelがその分のメモリ割り当てをする。そのため大きすぎる長さをreadに指定するとリソースを無駄に食う

Socket Options

Non-blocking IO

  • nonblock IOしたあと、読み込み書き込み可能になるまでを知るにはIO.selectを使う

Multiplexing Connections

  • IO.selectはfile descriptorが読み書き可能であるかを教えてくれる
  • select(2)は上限1024だけど、代わりにpollとかepollを使える

Framing Messages

  • どうやってメッセージを構成するか
  • 一行ごとにする
    • 拡張するとHTTP的な感じ
  • content lengthを先に送る方法

Timeouts

DNS Lookups

SSL Sockets

  • socketをSSLなsocketにすることは可能
  • SSLなsocketとnon SSLなsocketは同居できない

Urgent Data

  • 緊急のデータは他のデータより先に読み出したりすることが出来る
  • sendする方とrecvする方両方が緊急のデータに対応しておかないといけない
  • 1byteしか送れない制限ある

Network Architecture Patterns

Serial

  • 直列
  • connect -> handle -> disconnect -> ...
  • とにかくsimpleだけど、並列でないので遅い通信とかがあっただけでブロックする

Process per connection

  • 1つアクセスが来たらforkして実行
  • 非常にシンプルでわかりやすいが、プロセス数の上限を決められない

Thread per connection

  • processよりリソースの点で軽量

Preforking

  • 起動時に決められた数だけforkして、親はacceptせずに、子にacceptさせる
  • 上限決められるし、load balancingとかをkernelに任せられる
  • forkし過ぎるとメモリ周りで問題を抱える

Thread Pool

Evented (Reactor)

  • 仕組み??
    • 一つ制御用のTCP socketを作る
    • main loopでは一回のloopごとに読み取り可能socket, 書き込み可能socketを取り出す
    • それぞれのsocketを持つConnectionに対して、読み取り可能ならon_data、書き込み可能ならon_writableを呼び出し、Connectionに対して作業を通知する
    • 制御用TCP socketに対し、アクセスが来たら、それは新規connectionなので、新しくConnectionを作る
  • ほかと比べて並列度は高くできるけど、実行時にブロックする処理を一つでも書いたら死ぬ

Hybrids

  • とにかく様々なproductは色々な最適化はなされているけど、コアの部分はこれまで出てきたパターンの組み合わせで出来ている
    • Serial, Process per connection, Thread per connection, Preforking, Thread Pool, Evented (Reactor)
  • nginxはprefork、かつ子プロセスがEvented pattern