読者です 読者をやめる 読者になる 読者になる

select

selectは、複数のファイルディスクリプタを監視し、なんらかの状態変化が発生した場合に、この関数を抜けることで知らせるUNIXシステムコール・・・。

Man page of SELECT

これで、最大5つのクライアントを同時接続できるサーバをつくる・・・。

クライアントは、connect.cに、

このようなsendrecvループを持つような形で作成・・・。
今回のクライアントは"fin"でクライアントを終了するだけ・・・。

でサーバのselect.cだが、

最大5つのアクセプトソケットを格納できるようにclient_accを要素数5の配列にしている・・・。
また、fd_set型mask、struct timeval型timeoutも重要な要素・・・。

リッスンした後、まだアクセプトソケットが格納されていないということを示すために、client_accをすべて-1に初期化・・・。

ここからは、アクセプトループに入る・・・。
アクセプトループは、サーバが終了するまで、クライアントの受付をし続けるループ・・・。

まずはFD_ZEROでマスクを初期化・・・。

FD_SETでサーバソケットとマスクを紐付ける・・・。これでサーバソケットがまず監視対象になる・・・。

rangeは監視するソケットファイルディスクリプタの範囲を指定するものだが、まだサーバソケットしか監視しないので、soc+1とする・・・。

アクセプトしたソケット数を表すのがcount・・・。
まだアクセプトしてないのでcountは0、つまりこの監視対象ソケットの範囲を広げる部分は実行されない・・・。
しかし、countが増えるごとにFD_SETでアクセプトソケットがマスクに追加されていき、rangeも一番大きいアクセプトソケットファイルディスクリプタの値+1となる・・・。

select1回で何秒監視するかをtimeoutにセットする・・・。
tv_secは秒、tv_usecはミリ秒・・・。
ここでは5秒監視としている・・・。

selectにrange, mask, timeoutなどをセットする・・・。
戻り値が負の値ならエラー、0の場合タイムアウトとなる・・・。(今回は5秒過ぎたということ・・・。)
それ以外なら、何らかの状態変化があったということ・・・。

FD_ISSETで、変化が起きたソケットが指定されたものかどうかがわかる・・・。
socつまりサーバソケットの場合、acceptでアクセプトし、アクセプトソケットファイルディスクリプタを取得する・・・。
アクセプトしたクライアント情報を出力したら、client_accの空いてる場所に、アクセプトソケットファイルディスクリプタを格納・・・。

アクセプトソケットの場合、どのアクセプトソケットかを特定し、アクセプトソケットからデータを受信したり、データを送信し返したりする・・・。
"end"が入力されたら、server_exitを1にする・・・。

server_exitが1になったら、サーバ停止となる・・・。

すべてが終わった時、

クライアントソケット、サーバソケットすべてを閉じる・・・。

$ ./select
soc = 3
setsockopt(SO_REUSEADDR) success.
bind success.
listen success.
count = 0
timeout!
count = 0
timeout!
count = 0
timeout!
count = 0
timeout!
count = 0

サーバを起動すると、なにも起きてないので、count=0でtimeout!し続ける・・・。

別のターミナルから、connectクライアントで

$ ./connect
hostname: localhost
port: 3000
connect success.
>abc
abc
>hoge
hoge
>

接続して、abcやhogeを入力すると、

accept!(IPAddress = 127.0.0.1, Port = 56848)
count = 1
timeout!
count = 1
abc
count = 1
hoge
count = 1
timeout!

countが増えて、接続してきたのがわかり、データも送られている・・・。

$ ./connect
hostname: localhost
port: 3000
connect success.
>foobar
foobar
>xyz
xyz
>

別のターミナルで接続し、foobar, xyzを入力・・・。

count = 2
foobar
count = 2
xyz

countが2になり、foobar, xyzが来ている・・・。

これで2つのクライアントを同時接続して通信できているのがわかる・・・。

Sample/select.c at master · bg1bgst333/Sample · GitHub
Sample/connect.c at master · bg1bgst333/Sample · GitHub