web-dev-qa-db-ja.com

bind()を呼び出すときにsockaddr_inをsockaddrにキャストするのはなぜですか?

bind() 関数はsockaddrへのポインターを受け入れますが、私が見たすべての例で、sockaddr_in構造が代わりに使用され、sockaddrにキャストされます。

struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...

どうしてsockaddr_in使用される構造体。なぜsockaddrを準備して渡すだけですか?

単なる慣習ですか?

38
sashoalm

いいえ、単なる慣習ではありません。

sockaddrはあらゆる種類のソケット操作の汎用記述子であり、_sockaddr_in_はIPベースの通信に固有の構造体です(IIRC、「in」は「InterNet」を表します)。私の知る限り、これは一種の「ポリモーフィズム」です:bind()関数は_struct sockaddr *_をとるふりをしますが、実際には、適切なタイプの構造が渡されると仮定します;私。 e。最初の引数として指定したソケットのタイプに対応するもの。

51
user529758

これは、bindはIPソケット以外のタイプのソケット、たとえば、sockaddr_unをタイプとして持つUnixドメインソケットをバインドできるためです。 AF_INETソケットのアドレスにはアドレスとしてホストとポートがありますが、AF_UNIXソケットにはファイルシステムパスがあります。

5
Magnus Reftel

この質問に非常に関連するかどうかはわかりませんが、Cで多くの時間を費やしていない多くの人が見て混乱するので、タイプキャストをより理解しやすくするための追加情報を提供したいと思いますそのようなタイプキャスト。

macOSを使用しているため、システムのヘッダーファイルに基づいて例を取っています。

_struct sockaddr_は次のように定義されます。

_struct sockaddr {
    __uint8_t       sa_len;         /* total length */
    sa_family_t     sa_family;      /* [XSI] address family */
    char            sa_data[14];    /* [XSI] addr value (actually larger) */
};
_

_struct sockaddr_in_は次のように定義されます。

_struct sockaddr_in {
    __uint8_t       sin_len;
    sa_family_t     sin_family;
    in_port_t       sin_port;
    struct  in_addr sin_addr;
    char            sin_zero[8];
};
_

非常に基本的なことから始めて、ポインタにはアドレスが含まれています。したがって、_struct sockaddr *_と_struct sockaddr_in *_はほぼ同じです。どちらも住所を保存するだけです。関連する違いは、コンパイラがオブジェクトを処理する方法だけです。

したがって、_(struct sockaddr *) &name_と言うときは、コンパイラをだまして、このアドレスが_struct sockaddr_型を指していることを伝えるだけです。


それで、ポインターが位置_1000_を指しているとしましょう。 _struct sockaddr *_がこのアドレスを格納する場合、構造定義に従ってメンバーを所有する_1000_からsizeof(struct sockaddr)までのメモリを考慮します。 _struct sockaddr_in *_が同じアドレスを格納する場合、_1000_からsizeof(struct sockaddr_in)までのメモリを考慮します。


そのポインターを型キャストすると、sizeof(struct sockaddr)までの同じバイトシーケンスが考慮されます。

_struct sockaddr *a = &name; // consider &name = 1000
_

ここで_a->sa_len_にアクセスすると、コンパイラは_1000_から_sockaddr_in_の場合と同じバイトサイズのsizeof(__uint8_t)にアクセスします。したがって、これは同じバイトシーケンスにアクセスする必要があります。

_sa_family_も同じパターンです。

その後、_struct sockaddr_(typedef 'd 16ビット符号なし整数= 2バイト)からのデータを格納する_in_port_t sin_port_に14バイトの文字配列があり、_struct in_addr sin_addr_(単に32ビットipv4アドレス= 4バイト)および_char sin_zero[8]_(8バイト)。これら3つを合わせて14バイトにします。

これらの3つはこの14バイトの文字配列に格納されており、適切なインデックスにアクセスして再度型キャストすることにより、これら3つにアクセスできます。

user529758の答えは、これを行う理由をすでに説明しています。

2
Mihir