マルチクライアントプログラム


これまでのプログラムは、サーバーとクライアントで1対1の通信を行うものでした。 今度は、複数のクライアントを同時に扱うことのできるようにサーバを拡張します。 INETドメインでSTREAM型のプログラムを使います。 では、さっそくプログラムの例を見てみましょう。クライアントプログラムの方は、 INETドメインの解説で用いたのと全く同じものです。

まず、socket descriptorとしてs[SOCK_MAX+1]を定義しています。 SOCK_MAXは、自分でdefineしたもので、とりあえず5としてあります。 すなわち、全部で六つのソケットを用意したことになります。

mserverではこれらの六つのソケットを、

       s[0]     新しい接続要求を待つためのソケット
       s[1..4]  クライアントと接続され実際に通信をおこなうためのソケット
       s[5]     接続拒否に使うの一時接続ソケット
として使うことにします。また、s[i]の値がUNUSEDの場合は、そのソケット は使われていないことを表すことにします。

次にfd_set型の変数を定義します。今回はreadfdsという名前にします。 これは、後で述べるselectシステムコールで利用します。

接続を待つソケット(s[0])に関しては、socket、bind、listenシステムコール までの手順はiserverの時と全く同じです。

ここで、マルチクライアントで起こりうることを考えてみましょう。

最初のクライアントの接続は、acceptしてあげればうまくいきます。 しかし、次のクライアントのために、s[0]は再度acceptシステムコールで待ち状態 にしなければなりません。しかしながら、acceptシステムコールで 処理をブロックしてしまった場合、最初に繋いだクライアントの応対をする ことができなくなります。

そこで使うのがselectシステムコールです。これは、複数のソケット に対してのメッセージ待ちを可能にします。selectシステムコールでは 検査対象のソケットを指定し、そのうちのどれかにメッセージが到着する までブロックします。そしてどれかにメッセージが到着すると、どこに 届いたかを教えてくれます。

s[0], s[1]のどっちかにメッセージが来たら知らせるようにselectシステムコールを 設定し、処理が戻ると例えばs[1]にメッセージが到着しているということが わかります。ですから、それにしたがってメッセージの到着したソケット に対して処理を行なえばいいわけです。

selectシステムコールを使うにはfd_setという型の変数を使います。この変数で 検査対象となるソケットをあらかじめ指定するわけです。 このfd_set型の変数は直接操作せず、マクロを使って操作します。

まず、FD_ZERO(& readfds)でリストをクリアしておきます。そのあと、 検査対象はFD_SET(追加したいソケット,& readfds)を使って設定していきます。 selectシステムコール本体は

	select(FD_SETSIZE, & readfds, NULL, NULL, NULL)
となります。FD_SETSIZEはシステムで許される最大の個数をさします。 検査しないものにはNULLを指定します。 最後の引数はタイムアウト時間ですが、NULLを設定すると ready状態になるまでブロックされます。

selectシステムコールから処理が戻ると、readfdsにはFD_SETで設定したもの のうち、メッセージが到着しているものだけ、ビットがセットされて返ってきます。 これをFD_ISSET(調べるソケット, & readfds)を使ってどれがセットされているのか 調べます。

mserverでは、s[1〜max未満]のソケットにメッセージが到着しているかどうかを 調べ、到着している時は大文字に変換した後、接続中のクライアントすべてに メッセージを送り返しています。

ソケットs[0]は接続要求のくるソケットですから、このソケットにメッセージ が到着しているときにはacceptシステムコールで接続を受け付けます。また、 接続を拒否する場合には、一旦acceptしたあとに、ソケットをcloseして しまうという方法を使います。

以上でマルチクライアントのサーバが書けます。このmserverでは一度使って しまったソケットを再利用していませんが、これをちゃんと再利用するように すれば4クライアントのサーバとなります。


◆実行方法

  1. まず、何台かのマシンにログインします。(同じマシン上に複数のウィンドウを開いて 使っても構いません)

  2. 1台のマシンで、
        # ./server
    
    として先にサーバーを起動します。

  3. 次に残っているマシンで、
        # ./client サーバー名
    
    として、クライアントを起動します。

  4. クライアントのマシンで文字を打ち込むと、大文字に変換されて表示されます。

  5. 同時に複数のマシンでクライアントを起動しても、同じようにサーバーが処理してくれます。