190 likes | 458 Views
第 9 回ネットワークプログラミング. 中村 修. 今日のお題. 講義 select サーバの挙動:非同期多重入出力 select の使い方 練習 1 : select に慣れよう チャットクライアントを理解しよう --------- 休憩 -------------------------------- 実習: チャットサーバを作ろう. 非同期多重入出力. ファイルディスクリプタの状態 読み書きができるのか? 待たなければいけないのか? 入出力の状態は 非同期 そのプロセスだけで決めることができない 用意の出来たファイルディスクリプタ
E N D
第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 1 1 0 1 0 0 0 0 fd_set rfds FD_ZERO(&rfds) 0 0 0 0 0 0 0 0 fd_set rfds
FD_SET • fdset中のfdのbitをセットする→select()で監視すべきファイルディスクリプタをfd_setに入れる 標準入力が読み込み可能かselectの監視対象に入れる場合 fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7 0 0 0 0 0 0 0 0 fd_set rfds FD_SET(0, &rfds) 1 0 0 0 0 0 0 0 fd_set rfds
FD_CLR • fdset中のfdのbitをクリアする→FD_SETの反対 fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7 1 0 0 0 0 0 0 0 fd_set rfds FD_SET(0, &rfds) 0 0 0 0 0 0 0 0 fd_set rfds
FD_ISSET • fdset中のfdのbitがセットされているか検査する fd0 fd1 fd2 fd3 fd4 fd5 fd6 fd7 1 0 0 0 0 0 0 0 fd_set rfds 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 1 0 0 1 1 1 1 1 監視対象にFD_SETでbitを立てる fd_set rfds select(maxfdpl, &rfds, NULL, NULL,NULL) 0 0 0 0 1 0 0 1 用意の出来ているfdだけセットされいてる fd_set rfds
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