ソケット (BSD)

ソケット（，通信端点）とは、BSD系UNIXを起源とするAPIであり、C言語によるアプリケーション開発でのプロセス間通信、特にコンピュータネットワークに関するライブラリを構成する。その起源を強調してBSDソケット、バークレーソケットなどとも呼ばれる。
1983年にリリースされたUNIXオペレーティングシステム (OS) 4.2BSD で初めて API として実装された。ネットワークの抽象化インタフェースとしてのデファクトスタンダードとなっている。伝統的なSocket APIはC言語を対象とするが、他のプログラミング言語でも類似のインタフェースを用意している事が多い。
ソケットの代替となるAPIとして、STREAMSベースの Transport Layer Interface (TLI) がある。しかし、BSDソケットは比較にならないほど普及しており、数多くの実装が存在する。
BSDソケットインタフェース.
BSDソケットは、ホスト間の通信や1つのコンピュータ上のプロセス間の通信を可能とする。通信媒体としては様々な入出力機器やデバイスドライバを利用可能だが、その部分はオペレーティングシステムの実装に依存する。このインタフェース実装はTCP/IPを利用する際にはほとんどの場合で必要とされ、インターネットを支える基盤技術の一つとなっている。当初、カリフォルニア大学バークレー校でUNIX向けに開発された。最近の全てのオペレーティングシステムには間違いなくBSDソケットが何らかの形で実装されており、インターネットへの接続の標準インタフェースとなっている。
プログラマの観点から見ると、ソケットインタフェースは3つのレベルでアクセス可能である。最も強力で基本的なレベルは RAW（生）ソケットレベルである。RAWソケットが可能とする通信制御の自由度を必要とするアプリケーションは稀であり、インターネット関連技術の開発でのみ使われるべきとされている。
socketインタフェースは様々なネットワークプロトコルを抽象化している。これらのプロトコル群はprotocol familyとsocket typeに基づいてグループ分けされる。Linuxカーネルでは20を超えるprotocol familyが設定されており、例えばIPv4を指定するcodice_1やUNIXドメインソケットを指定するcodice_2がある。全てのprotocol familyはcodice_3で定義されている。socket typeの例にはcodice_4やcodice_5がある。familyとsocketの組み合わせでトランスポート層プロトコルに相当することが多い。
ヘッダファイル.
BSDソケットにはいくつかのヘッダファイルがある。
TCP.
TCP はコネクションの概念を提供する。TCPソケットを生成するには codice_11 関数で codice_1 または codice_13 と codice_4 を引数に指定する。
サーバ.
単純なTCPサーバの設定は、以下のように行われる。
C99 でのサーバー側のソースコード。
int main(void)
 // サーバーソケット作成
 int sock = socket(AF_INET, SOCK_STREAM, 0);
 if (sock == -1)
 perror("socket");
 return 1;
 // struct sockaddr_in 作成
 struct sockaddr_in sa = {0};
 sa.sin_family = AF_INET;
 sa.sin_port = htons(1100);
 sa.sin_addr.s_addr = htonl(INADDR_ANY);
 // バインド
 if (bind(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_in)) == -1)
 perror("bind");
 goto bail;
 // リッスン
 if (listen(sock, 128) == -1)
 perror("listen");
 goto bail;
 while (1)
 // クライアントの接続を待つ
 int fd = accept(sock, NULL, NULL);
 if (fd == -1)
 perror("accept");
 goto bail;
 // 受信
 char buffer[4096];
 int recv_size = read(fd, buffer, sizeof(buffer) - 1);
 if (recv_size == -1)
 perror("read");
 close(fd);
 goto bail;
 // 受信内容を表示
 buffer[recv_size] = '\0';
 printf("message: %s\n", buffer);
 // ソケットのクローズ
 if (close(fd) == -1)
 perror("close");
 goto bail;
bail:
 // エラーが発生した場合の処理
 close(sock);
 return 1;
クライアント.
TCPクライアントの設定は、以下のように行われる。
C99 でのクライアント側のソースコード。
int main(void)
 // ソケット作成
 int sock = socket(AF_INET, SOCK_STREAM, 0);
 if (sock == -1)
 perror("socket");
 return 1;
 // struct sockaddr_in 作成
 struct sockaddr_in sa = {0};
 sa.sin_family = AF_INET;
 sa.sin_port = htons(1100);
 // localhost の IP アドレスを引く
 struct hostent *hostent = gethostbyname("localhost");
 if (hostent == NULL)
 herror("gethostbyname");
 goto bail;
 memcpy(&sa.sin_addr, hostent->h_addr_list[0], sizeof(sa.sin_addr));
 // 接続
 if (connect(sock, (struct sockaddr*) &sa, sizeof(struct sockaddr_in)) == -1)
 perror("connect");
 goto bail;
 // 送信
 if (write(sock, MESSAGE, strlen(MESSAGE)) == -1)
 perror("write");
 goto bail;
 // クローズ
 close(sock);
 return 0;
bail:
 // エラーが発生した場合の処理
 close(sock);
 return 1;
上記のソースコードは gcc の場合 codice_50 をつけることによりコンパイル可能であるが、codice_51 を使用した場合は最初から定義されているので codice_52 はあっても無くてもどちらでも良い。codice_52 が無いと codice_54 が使えない。
UDP.
UDP はコネクションレスのプロトコルであり、パケットが必ず相手に届くことは保証されない。UDPパケットは順序通りに届くとは限らず、1つのパケットが複数個届くこともありうるし、全く届かないこともある。このようにほとんど何も保証されないため、UDP は TCP に比べてオーバヘッドが小さい。コネクションレスであるとは、2つのホスト間にコネクションやストリームといったものがなく、データが単に個々のパケット（datagram）として届けられることを意味している。
UDP のアドレス空間、すなわちUDPポート番号は、TCPポートとは別物である。
サーバ.
C99 でのサーバ側のソースコード。ポート番号 7654 で UDP サーバを開く。
int main(void)
 // ソケット作成
 int sock = socket(AF_INET, SOCK_DGRAM, 0);
 if (sock == -1)
 perror("socket");
 return 1;
 // struct sockaddr_in 作成
 struct sockaddr_in sa = {0};
 sa.sin_family = AF_INET;
 sa.sin_port = htons(7654);
 sa.sin_addr.s_addr = htonl(INADDR_ANY);
 // バインド
 if (bind(sock, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) == -1)
 perror("bind");
 goto bail;
 while (1)
 // 受信
 char buffer[4096];
 socklen_t addrlen = sizeof(struct sockaddr_in);
 ssize_t recv_size = recvfrom(sock, (void *) buffer, sizeof(buffer) - 1, 0, (struct sockaddr *) &sa, &addrlen);
 if (recv_size == -1)
 perror("recvfrom");
 goto bail;
 // 受信内容表示
 buffer[recv_size] = '\0';
 printf("datagram: %s\n", buffer);
 
bail:
 // エラーが発生した場合の処理
 close(sock);
 return 1;
codice_16 は、ソケットとアドレス/ポートを結びつける。
最後の無限ループは codice_56 を使ってポート番号 7654 からのUDPパケットを受信する。引数は以下の通り。
クライアント.
C99 でのクライアント側のソースコード。ポート 7654、アドレス 127.0.0.1 に "Hello World!" という内容の UDP パケットを送る。
int main(void)
 // ソケット作成
 int sock = socket(AF_INET, SOCK_DGRAM, 0);
 if (sock == -1)
 perror("socket");
 return 1;
 // struct sockaddr_in 作成
 struct sockaddr_in sa = {0};
 sa.sin_family = AF_INET;
 sa.sin_port = htons(7654);
 sa.sin_addr.s_addr = inet_addr("127.0.0.1");
 // 送信
 int bytes_sent = sendto(sock, MESSAGE, strlen(MESSAGE), 0, (struct sockaddr*) &sa, sizeof(struct sockaddr_in));
 if (bytes_sent == -1)
 perror("sendto");
 goto bail;
 // クローズ
 close(sock);
 return 0;
bail:
 // エラーが発生した場合の処理
 close(sock);
 return 1;
UNIXドメインソケット.
IP の場合は IP アドレス＋ポート番号で接続先を指定するのに対して、UNIXドメインソケットの場合はファイルパスで指定する。
関数.
socket().
codice_11 は通信の一方の終端を作成し、それを表すファイル記述子を返す。codice_11 には以下の3つの引数がある。
エラーが発生すると -1 を返す。そうでない場合、新たに確保された記述子を表す整数を返す。
int socket(int domain, int type, int protocol);
gethostbyname() と gethostbyaddr().
codice_77 と codice_78 は名前やアドレスで指定されたインターネット上のホストを表す codice_79 というデータ構造へのポインタを返す。その中には、ネームサーバから得られた情報、/etc/hosts ファイルから得られた情報などが格納されている。ローカルにネームサーバが動作していない場合、/etc/hosts ファイルを参照する。以下の引数がある。
エラーが発生するとNULLポインタを返し、codice_88 を見れば詳しいエラーの原因（再試行すべきか否か）がわかる。そうでない場合、有効な codice_89 が返される。
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const void *addr, int len, int type);
connect().
codice_37 はコネクションの確立に成功すると 0 を返し、エラーが発生すると -1 を返す。
コネクションレスのソケットの場合（User Datagram Protocol）、codice_37 は送受信の相手を指定するのに使われ、codice_30 と codice_31 をコネクションレスのソケットで使えるようになる。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind().
codice_16 はソケットにアドレスを設定する。codice_11 で生成された時点では、ソケットはアドレスファミリは指定されているが、アドレスは設定されていない。ソケットは、コネクションを受け付ける前にバインド（アドレス設定）される必要がある。以下の引数がある。
成功すると 0 を返し、エラーが発生すると -1 を返す。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen().
codice_27 はバインドされたソケットでコネクション確立要求を待ち受ける。codice_4 または codice_66 の場合のみ有効。以下の引数がある。
コネクションは codice_28 されるとデキューされる。成功すると 0 を返す。エラーが発生すると -1 を返す。
int listen(int sockfd, int backlog);
accept().
codice_28 は、リモートホストからのコネクション確立要求を受け付ける。以下の引数がある。
コネクションが確立された場合、それに対応したソケット記述子を返す。エラーが発生すると -1 を返す。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
ブロッキングとノンブロッキング.
BSDソケットは、ブロッキングとノンブロッキングという2つのモードを持つ。ブロッキング・ソケットは指定されたデータを全て送受信し終わるまで呼び出したライブラリ関数から戻ってこない。これは、ソケットで listen を続けたい場合に困ったことになる。プログラムは決して届かないデータを待って動けなくなる可能性がある。
ブロッキングかノンブロッキングかを指定するには、codice_117 か codice_118 関数を使う。
注意点.
codice_11 で確保されたリソースは codice_34 を使うまで解放されない。これは、codice_37 が成功するまで再試行を繰り返すような実装で注意が必要となる。codice_11 を呼び出したら必ず対応する codice_34 を呼び出す必要がある。close を使うには <unistd.h> をインクルードする必要がある。
参考文献.
ソケットインタフェースの正当な標準定義は以下の POSIX 標準に含まれている。
この標準に関する情報と現在進行の作業については、the Austin website にある。
ソケットAPIのIPv6拡張は、RFC 3493 と RFC 3542 にある。