Shammer's Philosophy

My private adversaria

Socket 構造体

いくつかのサイト等参考にして、C で EchoServer などの簡単なプログラムを書いてきたけれども、似たような名前の構造体がたくさん出てきて、おまじない感覚で書いているので少し調べてみた。大きく、以下の3つがあるようだ。

  • sockaddr
  • in_addr
  • sockaddr_in

sockaddr 構造体

これは汎用の構造体で、Socket のアドレス指定に使用する。以下のような宣言。

struct sockaddr
{
  unsigned short sa_family;
  char sa_data[14];
};

sa_familiy は、アドレスの種類を意味する。ということは、TCP/IP だと IP Address ということになる。AF_INET が IP のことだから、ここには毎回 AF_INET を指定することになるだろう。AF はおそらく Address Family の略でINET というのはインターネットの略、つまり、IP(Internet Protocol)のことだろう。

しかし、sockaddr というのはあくまでも汎用的な構造体で、TCP/IP 以外のプロトコルを使用するようになってもこの構造体を使用できるように考えられているらしい。TCP/IP 用に定義された構造体が sockaddr_in で、その sockaddr_in の中で具体的な IP Address を格納するための構造体として in_addr も定義されている、という関係のようだ。

sockaddr 構造体の sa_family は short だから 2 bytes、つまり、16 bits だ。ここはおそらく上記のアドレス体系ごとにそのアドレス体系を意味する bit が立つのだろう。2 の 16 乗個のアドレス体系に対応できることになるがおそらく TCP/IP を置き換えるものは今後当分の間出てこない気がする。自分が技術者やっている間は、きっとここには AF_INET を指定し続けることになるだろう。なお、AF_INET とまったく同じ値の PF_INET というラベルもあるようだが、値は同じため実質的には一つのアドレス体系(TCP/IP)しかないことになりそう。

sa_data は、14 bytes 確保されている。この中のどの程度を使用するかはアドレス体系ごとに変わってくるという話になるが、現時点ではアドレス体系は TCP/IP のみ。TCP/IP だと、アドレスが IPv6 でも 64 bits で、ポートも 65536 までのはずだから 16 bits (合っている?)あれば足りるはずなので、合計 80 bits、つまり 10 bytes。残りの 4 bytes は未使用領域ってことになりそうだが、この領域は将来誕生するかもしれない新しいアドレス体系用にバッファをとってあるんだと思う。

in_addr 構造体

IP アドレスを格納するための構造体。以下のように定義されている。

struct in_addr
{
  unsigned long s_addr;
}

s_addr にはその Socket の IP Address が入ることになる。

sockaddr_in 構造体

これが C で TCP/IP の通信プログラムを書くときに定義すべき構造体。以下の様に定義されている。

struct sockaddr_in
{
  unsigned short sin_family;
  unsigned short sin_port;
  struct in_addr sin_addr;
  char sin_zero[8];
};

sin_family は、sockaddr と同じ格納場所ということになるが、PF_INET を指定するのかな。
sin_port は TCP のポート番号が格納される。sin_addr には IP Address が格納される。
残りは未使用のバッファ領域。上の計算と合わないけれど・・・in_addr がポインタで指定されたりすれば、実際のデータ長とアドレス番地のサイズが異なったりするのかもしれない。まあ、ここは突っ込まなくてもいいか。

まとめ

とりあえず、以下のような感じだろうか。もっと細かい部分はいろいろあるかもしれないけれど。

  • sockaddr の sa_data[14] の具体的な内容が sin_port と sin_addr と sin_zero になっている。
  • sockaddr を使用するか、sockaddr_in を使用するかは API の定義次第。

Socket プログラムを書く際には、Source の sockaddr_in と Client 側の sockaddr_in の双方が必要になって来そうだ。すでに書いたサーバー側は、PassiveOpen なので自分(サーバー側)のアドレスだけ定義しておけばよさそうだが、クライアントの場合は Source と Destination それぞれの sockaddr_in が必要になったりするのだろうか。この辺は他の情報源のサンプルを見たり実際に書いて掘り下げていくことになりそうだ。