データグラムソケットは、同期データ交換インタフェースを提供します。接続を確立するための必要条件はありません。各メッセージには、宛先アドレスが含まれます。図 2-2 は、サーバーとクライアント間の通信の流れを示しています。
次の図のサーバー側で示されている bind(3SOCKET) の手順は省略できます。
データグラムソケットは、「ソケットの作成」で説明しているように作成されます。特定のローカルアドレスが必要な場合、bind(3SOCKET) オペレーションが最初のデータ伝送よりも先行しなければなりません。それ以外の場合、データが最初に送信される際にシステムがローカルアドレスまたはポート (あるいはこの両方) を設定します。データの送信には、sendto(3SOCKET) を使用します。
sendto(s, buf, buflen, flags, (struct sockaddr *) &to, tolen);
s、buf、buflen、および flags パラメータは、コネクション型のソケットの場合と同じです。to と tolen の値は、意図するメッセージ受信者のアドレスを示します。ローカルにエラー条件 (到達できないネットワークなど) が検出されると、-1 が戻り、errno がエラー番号に設定されます。
データグラムソケット上のメッセージを受信するには、recvfrom(3SOCKET) を使用します。
recvfrom(s, buf, buflen, flags, (struct sockaddr *) &from, &fromlen);
呼び出しの前に、fromlen が from バッファーのサイズに設定されます。fromlen は、戻り時には、データグラムの配信元であるアドレスのサイズに設定されます。
データグラムソケットは、ソケットを特定の宛先アドレスと関連付けるために connect(3SOCKET) 呼び出しを使用することもできます。続いて、send(3SOCKET) 呼び出しを使用できます。宛先アドレスの明示的な指定がない状態でソケット上で送信されるデータはすべて、接続されたピアにアドレス指定され、そのピアから受信されるデータだけが配信されます。1 つのソケットで一度に許可されるのは、接続された 1 つのアドレスだけです。2 つ目の connect(3SOCKET) 呼び出しは、宛先アドレスを変更します。データグラムソケット上の接続要求は、ただちに返されます。システムは、ピアのアドレスを記録します。accept(3SOCKET) と listen(3SOCKET) は、データグラムソケットでは使用されません。
データグラムソケットが接続されている間、前の send(3SOCKET) 呼び出しからのエラーは非同期に返すことができます。これらのエラーは、そのソケットの後続のオペレーションで報告できます。また、getsockopt(3SOCKET) のオプションである SO_ERROR を使用してそのエラーステータスを問い合わせることもできます。
例 2-3 は、ソケットの作成、ソケットへの名前のバインド、およびソケットへのメッセージ送信によってインターネット呼び出しを送信する方法を示しています。
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #define DATA "The sea is calm, the tide is full . . ." /* * ここで、コマンド行引数から得られる名前を持つ受信者に * データグラムを送信する。 * コマンド行の書式: dgramsend hostname portnumber */ main(argc, argv) int argc, errnum; char *argv[]; { int sock; struct sockaddr_in6 name; struct hostent *hp; /* 送信を行うソケットを作成する。*/ sock = socket(AF_INET6,SOCK_DGRAM, 0); if (sock == -1) { perror("opening datagram socket"); exit(1); } /* * 「送信」先ソケットの、ワイルドカードを使用しない構造名。 * getinodebyname は、指定されたホストのネットワークアドレスを * 含む構造体を返します。ポート番号は、コマンド行から取得され * ます。 */ hp = getipnodebyname(AF_INET6, argv[1], AI_DEFAULT, &errnum); if (hp == (struct hostent *) 0) { fprintf(stderr, "%s: unknown host¥n", argv[1]); exit(2); } bzero (&sin6, sizeof (sin6)); bzero (&name, sizeof (name)); memcpy((char *) &name.sin6_addr, (char *) hp->h_addr, hp->h_length); name.sin6_family = AF_INET6; name.sin6_port = htons(atoi(argv[2])); /* メッセージを送信する。*/ if (sendto(sock,DATA, sizeof DATA ,0, (struct sockaddr *) &name,sizeof name) == -1) perror("sending datagram message"); close(sock); exit(0); }
例 2-4 は、ソケットの作成、ソケットへの名前のバインド、およびソケットからの読み取りによってインターネット呼び出しを読み取る方法を示しています。
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> /* * このプログラムは、データグラムソケットを作成し、そのソケッ * トに名前をバインドし、続いてそのソケットから読み取ります。 */ main() { int sock, length; struct sockaddr_in6 name; char buf[1024]; /* 読み取られるソケットを作成する。*/ sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock == -1) { perror("opening datagram socket"); exit(1); } /* ワイルドカードを使用して名前を作成する。*/ bzero (&sin6, sizeof (sin6)); name.sin6_family = AF_INET6; name.sin6_addr.s6_addr = in6addr_any; name.sin6_port = 0; if (bind(sock,(struct sockaddr *)&name, sizeof name) == -1) { perror("binding datagram socket"); exit(1); } /* 割り当てられたポート値を確認し、それを出力する。*/ length = sizeof(name); if (getsockname(sock,(struct sockaddr *) &name, &length) == -1) { perror("getting socket name"); exit(1); } printf("Socket port #%d¥n", ntohs(name.sin6_port)); /* ソケットから読み取りを行う。*/ if (read(sock, buf, 1024) == -1 ) perror("receiving datagram packet"); /* データが出力可能であると想定する。*/ printf("-->%s¥n", buf); close(sock); exit(0); }