UNIXドメインのプロセス間通信プログラムの例
いきなり、ネットワークを使って離れたマシン間で通信を行う (INETドメインのソケットを使う)ようなプログラムを書くのはちょっと難しいので、 まずは一つのマシンの中で起動されているプロセス同士で通信を 行う(UNIXドメインのソケットを使う)方法を説明します。
では、早速プロセス間通信を行うプログラムを書いてみましょう。 プロセス間通信を行うには当然、メッセージを送信する側と受信する側の二つのプログラム を書く必要があります。
まず最初は簡単なサーバークライアント型のプログラムを作ってみましょう。 クライアント側ではユーザーからのキー入力を受け付け、入力された文字列をサーバーに 送ります。サーバーは、受け取った文字列のアルファベットの部分を大文字に変換して クライアントに送り返します。
![]()
ソケットを使って通信を行うには、ソケットを作ったり、ソケットを使ってメッセージを 送信したりするための特別な関数が必要です。プログラムの中で使われている 関数をまとめると次のようになります。
![]()
ちょっと数が多いですが、これらの関数を覚えてしまえば、あとはどんなプログラムにでも 応用できますので頑張って下さい。 では、これらの関数について一つずつ説明していきましょう。
- ■ ソケットの生成(socket)
- ソケット通信を行なうにはまず、通信を行なうすべてのプロセスが それぞれ socket() システムコールによりソケットを用意する必要があります。
int socket(int domain, int type, int protocol)domainには、sys/socket.hで定義されている値を使います。 今回は、UNIXドメインのソケットを使うので、PF_UNIXを指定します。 INETドメインのソケットを使う場合は、PF_INETを指定します。
typeは同じくsys/socket.hで定義されている、SOCK_DGRAMまたはSOCK_STREAMを 指定します。 ここではとりあえず、SOCK_STREAMとしておきます。詳しくはあとで説明します。
protocolは通常は0にしておけば問題ないです。
socket() はソケットディスクリプタを返し、この値が bind()、listen()、 accept() の引数に用いられます。
- ■ ソケットアドレスの指定(bind)
- socket()システムコールで作成したソケットに対して名前を割り当てます。 ここで付けられた名前が、クライアントが接続要求の際に指定する 名前となります。
int bind(int s, struct sockaddr *name, int namelen)sには、socket()の返り値を渡します。
nameは、sys/socket.hで定義されているsockaddr構造体へのポインタに なっていますが、実際にはsocket()の引数のdomainによって、内容が異なります。 sokcet()の引数をPF_UNIXにした時は、sockadd_un構造体を使います。struct sockaddr_un { u_char sun_len; /* sockaddr len including null */ u_char sun_family; /* AF_UNIX */ char sun_path[104]; /* path name (gag) */ };sun\_familyには、PF\_UNIXを指定します。sun\_pathはソケットのパス名です。
bindの第3引数は構造体の大きさで、sizeof(name.sun\_family) + strlen(name.sun\_path)を指定します。
- ■ 接続の準備(listen)
- サーバーはlisten()システムコールにより、接続のための準備を行ないます。
int listen(int s, int backlog)sは、socketの返り値です。backlogは接続要求をいくつ受け付けるかを指定します。 最大値は通常5です。
- ■ 接続要求(connect)
- 次に、クライアントがサーバに対して connect() システムコールにより 接続要求を出し、サーバは accept() システムコールにより接続を受け入れます。
int connect(int s, struct sockaddr *name, int namelen)引数は、bind()のものと同じです。
- ■ 接続(accept)
- サーバー側では、クライアントのconnect要求をacceptによって受け入れます。
int accept(int s, struct sockaddr *addr, int *addrlen)sは、listenの時と同じでsocket()の返り値を渡します。 nameには空の構造体のポインタを渡します。accept()が成功すると、システムが 自動的に書き込んでくれます。
接続要求が完了すると accept()はクライアントと接続された新しいソケット ディスクリプタを返します。
サーバはクライアントとの通信にこの新しいディスクリプタを用い、 socket()コールの時に返された古いディスクリプタはもう必要なくなります。
- ■ 通信(write,read)
- 接続が完了した後は、write()、read() システムコールにより通信を行なう ことができます。
ssize_t write(int d, const void *buf, size_t nbytes)sはソケットディスクリプタ。bufは送信したいデータです。ssize_t read(int d, void *buf, size_t nbytes)sはソケットディスクリプタ。bufは受信したデータを置くバッファです。
◆実行方法
- まず、kterm等のウィンドウを二つ開きます。
- 片方のウィンドウで、
# ./serverとして先にサーバーを起動します。
- 次にもう一方のウィンドウで、
# ./clientとして、クライアントを起動します。
- クライアントのウィンドウで文字を打ち込むと、大文字に変換されて表示されます。
![]()
![]()
![]()