TCP と UDP は、ローカル IP アドレス、ローカルポート番号、外部 IP アドレス、 および外部ポート番号の 4 つを使用してアドレス指定を行います。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 を使用する)。
ワイルドカードアドレスは、インターネットファミリにおけるローカルアドレスのバインドを簡易化します。次のコード例は、特定のポート番号 MYPORT をソケットにバインドし、ローカルアドレスは指定しないままにします。
#include <sys/types.h> #include <netinet/in.h> ... struct sockaddr_in6 sin; ... s = socket(AF_INET6, SOCK_STREAM, 0); bzero (&sin6, sizeof (sin6)); sin.sin6_family = AF_INET6; sin.sin6_addr.s6_addr = in6addr_any; sin.sin6_port = htons(MYPORT); bind(s, (struct sockaddr *) &sin, sizeof sin);
ホスト上の各ネットワークインタフェースは、通常、一意の IP アドレスを持ちます。ワイルドカードローカルアドレスを持つソケットは、指定されたポート番号にあてたメッセージと、ホストに割り当てられた可能性のあるアドレスに送信されたメッセージを受信できます。たとえば、ホストにアドレス 128.32.0.4 と 10.0.0.78 を持つ 2 つのインタフェースがあり、ソケットが例 2-17 のようにバインドされている場合、プロセスは 128.32.0.4 または 10.0.0.78 にアドレス指定された接続要求を受け入れることができます。特定のネットワーク上のホストだけに接続を許可するには、サーバーは適切なネットワーク上のインタフェースのアドレスをバインドします。
同様に、ローカルポート番号は指定しないままにしておくことができます (0 を指定)。この場合、システムがポート番号を選択します。次に、特定のローカルアドレスをソケットにバインドし、ローカルポート番号を指定しないままにする例を示します。
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) (from の結果) または getpeername(3SOCKET) で見つけます。
ポート番号を選択するためにシステムが使用するアルゴリズムがアプリケーションに適さない場合もあります。これは、関連付けが 2 段階のプロセスで作成されるためです。たとえば、インターネットファイル転送プロトコルでは、データ接続は常に同じローカルポートから発生しなければならないと定めています。しかし、異なる外部ポートに接続することによって、関連付けの重複が避けられます。この場合、前のデータ接続のソケットが存在していると、システムは同じローカルアドレスとポート番号をソケットにバインドすることを許可しません。デフォルトのポート選択アルゴリズムを無効にするには、次に示すようにアドレスをバインドする前にオプション呼び出しを行う必要があります。
... int on = 1; ... setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on); bind(s, (struct sockaddr *) &sin, sizeof sin);
この呼び出しを行うと、すでに使用されているローカルアドレスをバインドできます。同じローカルアドレスとローカルポートを持つほかのソケットが同じ外部アドレスと外部ポートを持たないことをシステムが接続時に検証するため、この呼び出しは一意性という条件に違反することはありません。関連付けがすでに存在する場合、エラー EADDRINUSE が返されます。