Talkプログラムのヒント 1 CS-B3 ネットワークプログラミング &情報科学科実験I
このスライドについて このスライドでは皆さんがプログラムを書いたり,関数を調べたりする過程で行き詰ると予想される部分について簡単に解説します. このスライドの目的は自主学習のサポートであり,説明が簡略化されています.完全な理解には自主学習が必要なので注意してください.
目次 よくつまずくところ 解決方法の例 select()とは ファイルディスクリプタとは ファイルディスクリプタ 具体的な仕様 ファイルディスクリプタ 具体的な仕様 ファイルディスクリプタ 使用例 select()の引数 select()の動作 まとめ
よくつまずくところ こんなプログラムのままになっていませんか? while(1) { fgets()でキー入力を取得; キー入力しなければ send()で入力文字を送信; recv()で相手からメッセージを受け取る; } キー入力しなければ 次に進めない 送っている最中は キー入力,受信ができない 受信待ちの間は キー入力,送信ができない
解決方法の例 こんなプログラムが書けるとよいですね. while(1) { if (fgets()すべきならば) { } if (send()すべきならば) { send()で入力文字を送信; if (recv()すべきならば) { recv()で相手からメッセージを受け取る;
解決方法の例 少しだけ具体的に書くと… while(1) { select()の引数の設定; select(引数); if (FD_ISSET(標準入出力,ファイルディスクリプタの集合)) { fgets()でキー入力を取得; } if (FD_ISSET(send用ソケット,ファイルディスクリプタの集合)) { send()で入力文字を送信; if (FD_ISSET(recv用ソケット,ファイルディスクリプタの集合)) { recv()で相手からメッセージを受け取る;
select()とは ソケット,標準入出力,ファイルが読み込み可能か,書き込み可能かを調べるときに使う. あらかじめ 監視する目的 (読み込み,書き込み) 監視する対象(ソケットか,標準入力か…) Select()のタイムアウト を決めなければならない ファイルディスクリプタの知識が必要
ファイルディスクリプタとは select()が監視するもの = ファイルディスクリプタの集合の変化 ファイルディスクリプタ ソケット,標準入出力,ファイルを識別する値 socketID = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); これ 標準入出力のファイルディスクリプタ stdin:0, stdout:1, stderr:2 ファイルディスクリプタの集合 複数のファイルディスクリプタの値をまとめて管理するもの
ファイルディスクリプタ 具体的な仕様 fd_set型変数 ファイルディスクリプタの集合 fd_set set; select()実行前
ファイルディスクリプタ 具体的な仕様 ファイルディスクリプタの集合の操作用マクロ FD_ZERO(fd_set *set); 集合をリセットする FD_SET(int fd, fd_set *set); fdを集合に登録する FD_SET(STDIN_FILENO, fd_set *set); 標準入力を集合に登録する FD_ISSET(int fd, fd_set *set); fdが集合に登録されているか確認 FD_CLR(int fd, fd_set *set); fdを集合から取り除く
ファイルディスクリプタ 登録例 ※ソケットをsoketIDとする. fd_set socketSet; ファイルディスクリプタの集合を作成 FD_ZERO(&socketSet); ファイルディスクリプタ集合の初期化 FD_SET(socketID, &socketSet); ソケットを集合に登録する FD_SET(STDIN_FILENO, &socketSet); stdinを集合に登録する 変数socketSetをselect()に渡せばソケットやstdinから文字列を受け取れるか確認できる.
select()の引数 ファイルディスクリプタの集合 ファイルディスクリプタは3種類指定できる. ファイルディスクリプタの集合(読み込み可能かどうかを調べる), ファイルディスクリプタの集合(書き込み可能かどうかを調べる), ファイルディスクリプタの集合(例外が発生したかどうかを調べる), タイムアウト); ファイルディスクリプタの集合 ファイルディスクリプタは3種類指定できる. それぞれ,読み込み可能かどうか,書き込み可能かどうか, 例外が発生したかどうかを監視するためにある. 読み込み可能かどうかを調べるためにselect()が用いられることが多い.
select()の引数 ファイルディスクリプタの最大値 + 1 ファイルディスクリプタの集合(読み込み可能かどうかを調べる), ファイルディスクリプタの集合(書き込み可能かどうかを調べる), ファイルディスクリプタの集合(例外が発生したかどうかを調べる), タイムアウト); ファイルディスクリプタの最大値 + 1 3種類のファイルディスクリプタの集合のうち,最も値の大きいファイルディスクリプタの値 例) socket1 と socket2の監視をしたい場合 socket1 > socket2 ならば socket1 + 1 socket1 < socket2 ならば socket2 + 1 タイムアウト この時間を過ぎるとselect()の待機状態は終了する. (※詳細はstruct timevalについて調べてください)
select()の動作 ファイルディスクリプタの集合に登録されたソケットや標準入力に変化があるまで待機する. FD_ISSETを用いれば,どこから文字列を受け取ればよいかわかる
Select関数を用いた並列処理の流れ fd_set socketSet FD_ZEROでsocketSetの初期化 socketID stdin FD_SETでsocketSetへの登録 socketID stdin select(socketID+1,&socketSet,NULL,NULL,&tv) 集合内のどれかが読み込み可能となるまで待機 socketIDからの読み取りが可能となった ⇒select関数は集合からsocketID以外を削除する socketID FD_ISSETでどのファイルディスクリプタが 集合内に残っているかを判別⇒各種処理
select()の動作 最初に見たプログラムの意味が分かってきた気がしませんか? while(1) { select()の引数の設定; if (FD_ISSET(標準入出力,ファイルディスクリプタの集合)) { fgets()でキー入力を取得; } if (FD_ISSET(send用ソケット,ファイルディスクリプタの集合)) { send()で入力文字を送信; if (FD_ISSET(recv用ソケット,ファイルディスクリプタの集合)) { recv()で相手からメッセージを受け取る;
select()の動作 注意(重要) ファイルディスクリプタの集合の設定はselect()を実行するたびに行ってください つまり while(1) の無限ループ内で,select()実行前に毎回 ファイルディスクリプタ集合とタイムアウトの設定を行う必要がある
まとめ select()を使うとfgets(),send(),recv()を行うべきタイミングが分かる このスライドのヒントはあくまでも実装方法の一例です. ほかにも実装方法があるかもしれません. 以上の内容をヒントにして,自主学習やプログラムの実装を進めてみてください.