ローレベルネットワークプログラミング小話

  • ネットワークプログラミング(C++)をする際にひかかったこととかについて
  • ACK遅延アルゴリズム
    • 適切でないTCPウィンドウサイズが設定されている受信側ソケットに対してものすごい勢いでガンガンデータを送信するとうまく受信できない。
  • 通信相手との接続状況を監視したい
    • これを行う完全な機構はTCP/IPプロトコルには用意されていない。
    • チェック用のスレッドをひとつ作って、そのスレッドから相手に確認用データを任意の感覚で送信してみる。
      • 間違ってもメインスレッドからビジーループでやったりしちゃいけませんよ。
      • まぁ結局は「どの条件になったら切断とみなすか」というプロトコルを自分で考えて実装しなければならない。
        • ネットワークリソースやコンピューティングリソースを消費するようなやりかたはだめだとWinsockFAQにもある。(いい忘れたがWinsockメインに書いてる。でも他とほとんど共通するないようだと思う)
    • TCP/IPより上層のアプリケーション層(かな?)で自前のプロトコルを考え、ある要求に対しては必ず決まった応答が帰ってくるようにし、そうならないときには問題が発生していると判断する方法。
  • ポートは同じでもIPアドレスが違えば接続を複数acceptできる(あたりまえだけど)
    • accept処理を行う側のプログラムではポートごとにaccept処理用のスレッドを生成し、そこでマルチクライアントからの接続要求を処理する。
    • lisetn → accept の処理は正規のドキュメントにはじまりほかのさまざまな情報を熟読しないと初心者に正確な挙動を把握するのは難しいだろう。
      • よくある入門用コードは大体の流れをつかむにはいいが接続・接続解除等が頻発する処理を行うときに生まれるソケットプログラミングの複雑さの本質については何もわからないのでとにかくいじってみないとまともに動くものは作れない。
      • 最近はネットワークライブラリなんかも充実してそうだからあれだが、やはり自分で独自に処理を作りたいときというのはあるだろう。
  • 接続が切断されたときのリソース管理
    • これしっかりやらないとごちゃごちゃする
      • 常に通信相手との接続状態を監視し、突然の切断があってもしっかり後始末するように
  • ソケットオプション
    • 接続タイムアウト時間等を設定したくなるだろう。
    • そのときはsetsockoptでいろいろ出来る
    • しかしacceptのタイムアウト間隔を設定するには少し癖があり、かなり省いており恐縮ではあるが大体下のコードのような流れで行うことになる。ほかにも方法があるのかどうかは知らない。
      • acceptは呼び出した直後からそのスレッドでブロッキングモードに入るので場合によってはかなり迷惑だ。accept用のスレッド内でブロッキングしているとしてもそれが困るときもあるだろう。
      • 擬似コードだと思って読んでほしい
while( !finish_accepting )
{
 FD_ZERO( &sockets );
 FD_SET( sock, &sockets );

 timeval accepting_duration = {0};
 accepting_duration.tv_sec = 5;
 accepting_duration.tv_usec = 0;

 select( 0, &sockets, NULL, NULL, &accepting_duration );

 if( FD_ISSET( sock, &sockets ) )
 {
   client = accept( 
    sock, (sockaddr*)(&client_info), &sz 
   );
 }
}
  • ネットワークイベント
    • リード、ライト、クローズというソケットディスクリプタに対するイベントが用意されているので、イベントドリブンな処理をソケットに行いたい場合にはこのあたりを使うとスマートに実装できる。
    • 関数の戻り値から判断して・・・などという処理ばかりだと汚いコードになる場合もあるからね・・・
  • スレッド
    • ネットワークプログラミングにはスレッドが欠かせないと思う。まぁなくてもある程度までは出来るかもしれないがやはり無理が出てくるだろう。
    • よくネットワークプログラミングよりスレッドのほうが難しいなどと大げさに言う人がいるが、確かに難しい側面もある。
    • しかしスレッドといえどもシンプルな実装を心がけ、がむしゃらにスレッド処理を含んだ実装を行わなければそれほど難のあるものでもないのであまりびびらないように
      • まぁ基本的なことだがスレッドでループを回す際に、タイムウェイトを入れ忘れてビジーループを作ってしまったりしないように(たまにやったりする)。
      • 別スレッドといえども完全なビジーループが入るとシングルスレッドでスピンロックしているのと等しく動かなくなったりするので
    • 送信用の処理もスレッドにすべし
      • 送信と受信をともに行いかつ送信が超頻繁に行われる場合、受信イベントと送信イベントがかぶった場合受信がうまく出来なくなる。互いのデータスタックに干渉するわけではないが、受信自体ができない(たぶん)。まぁこの辺の厳密な裏づけはこの記事のリンクにあるQ&Aサイトでご確認を・・・。
      • 諸々あるのでやはりスレッドにすべき
    • スレッドライブラリはboostのスレッドライブラリが対応している環境であればこれを用いるのが便利。まぁpthreadやWindowsAPIもいいがとにかくboostのスレッドライブラリは使いやすい。初心者にはおすすめだ。ただ対象とする環境でのネイティブな方法を学習したいという場合、一度やっておくといい。
      • ネットワークとはネットワークプログラミングからは少し外れるがboostスレッドライブラリを用いるときにboost::thread::sleepを用いるときの注意点について
      • boost::thread::sleepはxtimeというクラスを引数にとる。このxtimeはsecとnsec(秒:ナノ秒)という属性を持っており、この値を指定してsleepに渡してやることでタイムウェイトが出来るわけだが、少し使い方に癖がある。boostのサンプルだと下記のような例が示されているが、このやり方だと1秒以上の間隔しか指定できない!
      • これは自前で処理しなければならんのか??? 否
  boost::xtime xt;
  boost::xtime_get( &xt, UTC_TIME );
  xt.sec += 1;
  //xt.nsec += 3000000; エラー。コンパイルエラーこそ出ないが動作しない。
  boost::thread::sleep( xt );
      • で、そうするかというと、boost::threadのソースにあるtimeconv.inlというファイルにその手の処理が書かれていてこれはインクルードファイルには含まれていないが、これを自分のソースにインクルードしてやり、
  boost::xtime xt;
  to_time( 300, xt );
  boost::thread::sleep( xt );
      • とすることで、1秒未満の精度でウェイトが実現できる。これもっと表に出してくれてもよくないか?
      • boostなので結構ジェネリックに作ってあり、いろいろマニアックな使い方をする場合にはそれなりに調べる必要がある。ライブラリに一緒についてくるサンプルが参考になる。
  • NATブレイク
    • これ、一昔前はかなりの曲者だった(今でも曲者ですが)。
    • 何かというとNAT(HUBとかルータ)経由でプライベートネットワークアドレス間の通信を行おうとすると、外部ネットワークからはルータの外部側IPアドレスしかわからないのでその内側にある端末のIPへはどうやってつないだらいいの?というもの。
      • 「外部IPアドレス」という言葉が出たが、通常NATはIPアドレスの枯渇を回避するためにプライベートネットワークを構築するのが主な目的のひとつなので、「外部ネットワークと通信するためのアドレス」と「内部ネットワークとNAT間で通信するためのアドレス」の2つを持つ。
      • そうです。NAT自体もいろいろとデータを受け取り、ネットワークの仲介に必要な処理が行えるちょっとした計算機なんです。でその処理の代表的なものが「IPマスカレード(呼び方古いか)」。
        • これはざっくりいうと「この外部IPアドレス:ポート番号で行う通信は内部ネットワークの端末からのこのポート番号での通信に使用するよ。というマッピングを行うもの。
      • しかしUPnPユニバーサルプラグアンドプレイ)が登場し、UPnP対応NAT機器に対してプログラマティカルにポートフォワード設定を行うことが比較的容易になった。プロトコルを理解する必要が少しあるが、つまみ食いしながらでもいいだろう。ほかにもNAT越えの方法としてはSTUN等があり、かなり使えるようだが完全に一般化されたものではなく問題点もある。
      • しかしNATブレイク関してはライブラリを用いるというのも楽でいいだろう
      • このサイトhttp://www.aa.alpha-net.ne.jp/bosuke/trash/upnp.htmはその辺実践的に解説してくれているので感覚をつかむにはナイスなサイトだ。さらに詳しい情報に関しては他の正式な文書やMicrosoftのヘルプ等を検索したほうがいいだろう。
        • ページ下にあるサンプルがナイス過ぎる
        • CodeProjectとかにもいくつかあるけどあそこにあるやつって結構ビルドに難があったりするんだよな〜
  • 書籍
    • ISBN:4797328479
      • オンラインゲームプログラミング・・・なんて名前なのでNATブレイクとかその辺のことも詳しく載ってるのかと思いきやまったくの期待はずれ。内容はまぁ基本的なことがだらだらかかれたのちに、ちゃんと動いているのかどうかも確認しづらいサンプルプロジェクトのソースコードが長々と載せられていたりする。まぁNAT問題についての情報が得られない時点で個人的には20点だが、基本的な事柄に対する辞書的存在としてはあってもいい本だ。
      • この著者も過去にNAT問題でライブラリを書き換える羽目になったと失敗談は語っているものの、NAT越えの実践技法に関しては「本質的」ではないから本書では述べない。などといっている。この辺に関しては正直「はぁ?」といいたくなる。そんな失敗を乗り越えたのならそれをネタにして書けと。めんどくさかったんだろうな。NATブレイクはまだ完全じゃないし。
      • 実はネットワークプログラミングではNATブレイクが最も面倒な問題のひとつ・・・だと私は思うので無念さが残る買い物だった。まぁ他で調べられたからもういい