アドレスを指定するとき、TCP と UDP は次の 4 つの要素を使用します。
ローカル IP アドレス
ローカルポート番号
外部 IP アドレス
外部ポート番号
TCP では、これらの 4 つの組は一意である必要があります。UDP にはこのような要求はありません。ホストは複数のネットワークに常駐でき、ユーザーは割り当てられているポート番号に直接アクセスできません。したがって、ユーザープログラムは必ずしもローカルアドレスとローカルポートに使用する適切な値を認識できるとは限りません。この問題を避けるため、アドレスの一部を指定せずにおき、必要に応じてシステムにこれらの部分を適切に割り当てることができます。これらの組の各部は、ソケット API のさまざまな部分によって指定できます。
ローカルアドレスまたはローカルポート (あるいはこの両方)
外部アドレスと外部ポート
accept(3SOCKET) 呼び出しは、外部クライアントからコネクション情報を取得します。したがって、accept(3SOCKET) の呼び出し元が何も指定していなくても、ローカルアドレスとローカルポートをシステムに指定できます。外部アドレスと外部ポートが返されます。
listen(3SOCKET) を呼び出すと、ローカルポートが選択されます。ローカル情報を割り当てる bind(3SOCKET) を明示的に指定していない場合、listen(3SOCKET) は一時的なポート番号を割り当てます。
特定のポート上に常駐するサービスを、そのポートに bind(3SOCKET) でバインドすることができます。このとき、ローカルアドレスは指定しないままにしておいてもかまいません。ローカルアドレスは、<netinet/in.h> に定数値を持つ変数 in6addr_any に設定されます。ローカルポートを固定する必要がない場合、listen(3SOCKET) を呼び出すと、ポートが選択されます。アドレス in6addr_any またはポート番号 0 を指定することを「ワイルドカード (を使用する)」と呼びます。 AF_INET の場合は、in6addr_any の代わりに INADDR_ANY を使用します。
ワイルドカードアドレスは、インターネットファミリにおけるローカルアドレスのバインドを簡易化します。次のコードは、getaddrinfo(3SOCKET) の呼び出しで返された特定のポート番号をソケットにバインドし、ローカルアドレスを指定しないままにしておく例です。
#include <sys/types.h> #include <netinet/in.h> ... struct addrinfo *aip; ... if (bind(sock, aip->ai_addr, aip->ai_addrlen) == -1) { perror("bind"); (void) close(sock); return (-1); }
ホスト上の各ネットワークインタフェースは、通常、一意の IP アドレスを持ちます。ワイルドカードローカルアドレスを持つソケットは、指定されたポート番号に宛てたメッセージを受信できます。ワイルドカードローカルアドレスを持つソケットはまた、ホストに割り当てられている可能性のあるアドレスに送信されたメッセージを受信できます。特定のネットワーク上のホストだけにサーバーとの接続を許可するために、サーバーは適切なネットワーク上のインタフェースのアドレスをバインドします。
同様に、ローカルポート番号を指定しないままにしておくと、システムがポート番号を選択します。たとえば、特定のローカルアドレスをソケットにバインドするが、ローカルポート番号は指定しないままにしておくには、次のように bind を使用します。
bzero (&sin, sizeof (sin)); (void) inet_pton (AF_INET6, "::ffff:127.0.0.1", sin.sin6_addr.s6_addr); sin.sin6_family = AF_INET6; sin.sin6_port = htons(0); bind(s, (struct sockaddr *) &sin, sizeof sin);
システムは、次の 2 つの基準でローカルポート番号を選択します。
1024 未満のインターネットポート番号 (IPPORT_RESERVED) は、特権ユーザー用に予約される。非特権ユーザーは 1024 を超えるインターネットポート番号を任意に使用できる。インターネットポート番号の最大値は 65535。
現在、ポート番号はほかのソケットにバインドされていない。
クライアントのポート番号と IP アドレスは accept(3SOCKET) または getpeername(3SOCKET) で確認します。
関連付けが 2 段階のプロセスで作成されるため、システムがポート番号を選択するために使用するアルゴリズムがアプリケーションに適さない場合もあります。たとえば、インターネットファイル転送プロトコルでは、データコネクションは常に同じローカルポートから実行する必要があると定めています。しかし、異なる外部ポートに接続することによって、関連付けの重複を避けることができます。この場合、前のデータコネクションのソケットが存在しているとき、システムは同じローカルアドレスとローカルポート番号をソケットにバインドすることを許可しません。
デフォルトのポート選択アルゴリズムを無効にするには、次に示すようにオプション呼び出しを行なってからアドレスをバインドする必要があります。
int on = 1; ... setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on); bind(s, (struct sockaddr *) &sin, sizeof sin);
この呼び出しを行うと、すでに使用されているローカルアドレスをバインドできます。この呼び出しは一意性という条件に違反しません。なぜなら、同じローカルアドレスとローカルポートを持つ別のソケットが同じ外部アドレスと外部ポートを持たないことをシステムがコネクション時に検証するためです。関連付けがすでに存在する場合、エラー EADDRINUSE が返されます。