第9回NP資料 select - WIDE University

第9回ネットワークプ
ログラミング
中村 修
今日のお題
 講義
select
 サーバの挙動:非同期多重入出力
 selectの使い方
 練習1:
selectに慣れよう
 チャットクライアントを理解しよう
---------休憩------------------------------- 実習: チャットサーバを作ろう
非同期多重入出力

ファイルディスクリプタの状態
 読み書きができるのか?
 待たなければいけないのか?

入出力の状態は非同期
 そのプロセスだけで決めることができない

用意の出来たファイルディスクリプタ
 読み込み準備の整ったファイルディスクリプタからだけ
読みたい
 書き込み準備の整ったファイルディスクリプタに対して
だけ書き込みたい

とにかく複数のディスクリプタから非同期に読み書き
がしたい
select()システムコール

複数のディスクリプタが非同期に有効になる場
合、どのファイルディスクリプタが有効なのかを
監視するシステムコール
 ファイルディスクリプタの集合を渡して、読み出し/
書き込み可能なファイルディスクリプタを教えてもら
う
 一定のタイムアウトも指定できる
select()関数
#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdpl, fd_set *readset, fd_set
*writeset, fd_set *exceptset,
const struct timeval *timeout)
戻り値:準備ができているディスクリプタの個数、
タイムアウトなら0、エラーの場合は-1
struct timeval{
long
tv_sec;
long
tv_usec;
}
fd_set型変数を操作するためのマクロ
void FD_ZERO(fd_set *fdset);
 void FD_SET(int fd, fd_set *fdset);
 void FD_CLR(int fd, fd_set *fdset);
 int FD_ISSET(int fd, fd_set *fdset);

FD_ZERO
fdset中の全てのbitをクリアする→fd_set型変
数の初期化に用いる
 FD_SETなどを行う前に必ず行う

fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7
fd_set rfds
1
1
0
1
0
0
0
0
FD_ZERO(&rfds)
fd_set rfds
0
0
0
0
0
0
0
0
FD_SET

fdset中のfdのbitをセットする→select()で監視
すべきファイルディスクリプタをfd_setに入れる
標準入力が読み込み可能かselectの監視対象に入れる場合
fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7
fd_set rfds
0
0
0
0
0
0
0
0
FD_SET(0, &rfds)
fd_set rfds
1
0
0
0
0
0
0
0
FD_CLR

fdset中のfdのbitをクリアする→FD_SETの反対
fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7
fd_set rfds
1
0
0
0
0
0
0
0
FD_SET(0, &rfds)
fd_set rfds
0
0
0
0
0
0
0
0
FD_ISSET

fdset中のfdのbitがセットされているか検査する
fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7
fd_set rfds
1
0
0
0
0
0
0
0
If(FD_ISSET(0, &rfds))
標準入力が読み込み可能な時の処理を行う
If(FD_ISSET(0, &rfds)){
read(0, &buf, sizeof(buf)); /*標準入力が読み込み可能な時の処理を行う*/
}
select()

あるfd_setが読み込み可能か監視する場合
select(maxfdpl, &rfds, NULL, NULL, NULL)
fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7
fd_set rfds
1
0
0
1
1
1
1
1
監視対象にFD_SETで
bitを立てる
select(maxfdpl, &rfds, NULL, NULL, NULL)
fd_set rfds
0
0
0
0
1
0
0
1
用意の出来ているfdだけ
セットされいてる
selectの基本的な使い方
まずはFD_ZERO
 監視対象をFD_SETする
 ループに入る
 selectで監視
 FD_ISSETで評価

練習:selectに慣れよう

以下のソース(簡易チャットクライアント)を
 コピー
 コンパイル
 実行しよう


/home/kaizaki/osamuNP/9/tcp_chat_client.c
実行例
 ./a.out
ccz03.sfc.keio.ac.jp
 自分の名前を打ち込んでみよう

ソースコードをじっくり理解しよう
重要ポイント1
fd_set target_fds; /* fd_set */
fd_set org_target_fds;
FD_ZERO(&org_target_fds);
FD_SET(sockfd, &org_target_fds);
FD_SET(0, &org_target_fds);
while (1) {
。。。。
重要ポイント2
while (1) {
target_fds = org_target_fds;
select(FD_SETSIZE, &target_fds, NULL,
NULL,&wait_time);
if(FD_ISSET(0,&target_fds)){
処理1;
}
if(FD_ISSET(sockfd, &target_fds)){
処理2;
}
}
覚えると便利:setsockopt
int sock_optval = 1;
if ( setsockopt(listen_fd, SOL_SOCKET,
SO_REUSEADDR,&sock_optval,
sizeof(sock_optval)) == -1 ){
perror("setsockopt");
exit(1);
}
 いろんなオプションがあるけど、これだけは覚えよう!
 Address already in Use を防げます
実習: チャットサーバを作ろう
 チャットシステムを作ろう!
サーバでselect()を使う
サーバ側動作




リスニングソケット(accept()の引数に入るソ
ケット)をselect()で監視
読み出し可能(read OK)になったら、accept()
を実行
新しく作られたファイルディスクリプタもselect()
のリストに追加
以上の動作を繰り返しながら、リスニングソ
ケットとaccept()したクライアント向けソケットの
状態を見て、リスニングソケットだったら
accept()、それ以外のソケットだったら全てのク
ライアントへメッセージの送信を行う
ヒント: 流れがわかるかも?

以下に、(ほぼ)コメントのみのソースコードあ
り。
/home/kaizaki/osamuNP/9/
select_server_comment_only.c