STREAM型とDGRAM型


これまでは触れませんでしたが、ソケットを使った通信には、STREAM型(またはコネクション型 ともいう)とDGRAM型(同じくコネクションレス型ともいう)の2種類があります。 前者は、通信プロトコルにTCPを用いており、後者はUDPを用います。 先程のプログラムの例では、前者のSTREAM型(コネクション型)を使っていました。 STREAM型の特徴は、 です。先程のの例では、socketシステムコールの第2引数に SOCK_STREAMを 指定しました。これは、STREAM型のソケットの使用をシステムに知らせていた のです。STREAM型は、接続を確立するという意味からコネクション型のソケッ トと呼ばれることもあります。socketシステムコールの説明では、 SOCK_STREAMのほかに SOCK_DGRAMという値も使われると説明しました。この 値を指定した場合は、STREAM型ではなくDGRAM型のソケットが得られます。

DGRAM型のソケットの特徴としては、

などが上げられます。こちらは、接続を確立しないのでコネクションレス型の ソケットとも呼ばれます。

こうしてみると、DGRAM型の特徴は、STREAM型とくらべて見劣りするものばか りです。にもかかわらず、多くのアプリケーションがDGRAM型のソケットを利 用しています。その理由の1つは、STREAM型に比較してDGRAM型のソケットの ほうが“軽い”からです。STREAM型はDGRAM型にはない機能を提供するため、 どうしても処理が複雑になってしまいます。そのため通信のオーバーヘッドが 大きくなります。

では実際に、DGRAM型を使って先程と同じプログラムを書いてみましょう。 使われる関数が少し変わります。

■ ソケットの生成(socket)
まず、socketシステムコールでソケットを生成します。第2引数を SOCK_DGRAMにして、DGRAM型のソケットを作ります。

■ ソケットアドレスの指定(bind)
次に、サーバー側でbindシステムコールを呼び、ソケットに名前を割り当てます。
ここまでは、前回のSTREAM型の場合と同じです。STREAM型の場合は、次にサー バー側で listen()とaccept()を、クライアント側でconncet()を呼びましたが、 DGRAM型の場合は、これで準備は終りです。

STREAM型の場合には、サーバーはaccept()により、通信に用いる別のファイル ディスクリプタを取得しました。しかしDGRAM型の場合は、サーバーが最初に 作成したソケットを用いてクライアントとの通信すべてをやりとりします。

この手順では、クライアントはsocket()とbind()を実行しているだけで、サー バー側のアドレス指定のためのシステムコールはまったく実行していません。 接続を確立しないのですから当然ですが、これではサーバーにデータを送れま せん。実際の通信にあたっては、クライアントはサーバーを指定する必要があ ります。相手を指定して通信をおこなうために、DGRAM型のソケットの場合は write()ではなくsendto()を使います。これは次のような形式で使います。

	ssize_t 
        sendto(int s, const void *msg, size_t len, int flags, 
                             const struct sockaddr *to, int tolen)
最初の引数sは、通信に利用するソケットを指定するファイルディスクリプタ です。sokcet()で生成されたディスクリプタを指定します。次のbufは送信す るバッファの先頭のアドレスを、lenは送信するバイト数を指定します。 ここまでの引数はwrite()と変わりません。

flagsには、sys/socket.hヘッダファイルで定義されているMSG_OOBと MSG_DONTROUTEを、論理和を用いて指定しますが、通常はこれらのフラグは設 定しないので、たんに0を指定します。

次のtoには通信相手を示すsockaddr構造体のアドレスを、tolenにはtoの大き さを指定します。この二つの引数を用いて、相手を指定するのです。この引数 の値は、STREAM型のソケットにおけるconnectシステムコールでの第2、第3 引数と同様に、通信相手側のソケットの名前を指定します。

sendto()の返り値は、実際に送信したバイト数です。なんらかのエラーが発生 すると-1が返されます。

データを送る場合に、write()ではなくsendto()を用いて通信相手を指定する 必要があるのは、すぐに理解できるでしょう。じつは、データを受け取る場合 にも、read()ではなく別のシステムコールを用いる必要があります。これは、 ソケットが接続されているわけではないので、誰からの通信かを知る必要があ るためです。一般のアプリケーションでは、なんらかの通信をおこなうとその 返答が同様に通信で返されます。データを受け取るときに誰からの通信か分ら なければ、計算した結果も返しようがありません。

このため、DGRAM型のソケットでは、データを受け取るときにread()ではなく recvfrom()システムコールを用います。これは、

	ssize_t
	recvfrom(int s, void *buf, size_t len, int flags, 
                         struct sockaddr *from, int *fromlen)
という形式で使用します。最初の引数sはデータを受け取るソケットを指定し ます。次のbufは受信したデータを格納するバッファを指定します。lenはその バッファの大きさです。ここまでの引数は、STREAM型ソケットを用いた場合の read()システムコールのものと同様です。

次のflagsはsendto()のものと同じで、通常は0にしておきます。fromには、デー タを送信した側のソケットの名前が、fromlenにはfromの大きさがシステムコー ルにより格納されて返されます。