Transcript 第9回授業
第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