第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
© Copyright 2024 ExpyDoc