4    ソケット

オペレーティング・システムのソケット・プログラミング・インタフェースは,XNS5.0 標準,XNS4.0 標準,POSIX 1003.1g Draft 6.6,および Berkeley Software Distribution (BSD) のソケット・プログラミング・インタフェースをサポートしています。 さらに,オペレーティング・システムは,RFC 2553 で定義されている IPv6 (Internet Protocol Version 6) 用の基本ソケット・インタフェース拡張ソケットに対応しています。 ソケット機能の基本の構文はそのままです。 既存の IPv4 のアプリケーションは以前同様に動作しますし,IPv6 のアプリケーションは IPv4 アプリケーションと相互運用することができます。

このオペレーティング・システムでは,ソケットは,インターネット・プロトコル群 (TCP/IP) および同じシステム上でのプロセス間通信の UNIX ドメインとのインタフェースを提供します。 ただし,ソケットを使用すると,基礎となるネットワーキング・プロトコルおよびハードウェアに依存しない,ネットワーク・ベースのアプリケーションを作成することができます。

プログラム中で XNS4.0 標準のインプリメンテーションを使用するためには,c89 コンパイラ・コマンドでプログラムをコンパイルしなければなりません。 詳細については, standards(5) を参照してください。 この章で使用する例は,XNS4.0 標準に基づいています。 XNS4.0,XNS5.0,POSIX 1003.1g Draft 6.6,および BSD の各インタフェースの相違点についての詳細は,4.5 節を参照してください。

この章では,次の事項について説明します。

図 4-1は,ソケット・フレームワークを強調表示して,ネットワーク・プログラミング環境における他の部分との関係を示しています。

図 4-1:  ソケット・フレームワーク

4.1    ソケット・フレームワークの概要

ソケット・フレームワークは,次のものから構成されます。

4.1.1    ソケットの通信プロパティ

この項では,ソケット通信プロパティの基礎となる,抽象表現およびその定義について説明します。

4.1.1.1    ソケット抽象表現

ソケットは,通信の終端として機能します。 単一のソケットは,1 つの終端です。 1 組のソケットで,双方向の通信チャネルを構成して,関連のないプロセス間でローカルに,またはネットワークを通して,データ交換を行うことができるようにします。

アプリケーション・プログラムは,必要に応じて,ソケットの作成をオペレーティング・システムに要求します。 オペレーティング・システムはソケット記述子を返すので,プログラムはこの記述子を使用して,新しく作成されたソケットを参照し,さまざまなオペレーションを行います。

ソケットには次の特性があります。

ソケットは,通信プロパティに応じてタイプが決まります。 使用できるソケット・タイプについては,4.1.1.3 項を参照してください。

4.1.1.2    通信ドメイン

通信ドメインによって,ハードウェアおよびソフトウェアが異なるシステム間の通信の意味が定義されます。 通信ドメインでは次のことを指定します。

オペレーティング・システムは,省略時に次のソケット・ドメインをサポートします。 [脚注 16]

表 4-1は,UNIX ドメインおよびインターネット・ドメインの特性をまとめたものです。

表 4-1:  UNIX 通信ドメインおよびインターネット通信ドメインの特性

  UNIX インターネット
ソケット・タイプ SOCK_STREAM, SOCK_DGRAM SOCK_STREAM, SOCK_DGRAM, SOCK_RAW。
名前 ASCII 文字列。 たとえば,/dev/printer 32 ビットの IP Version 4 アドレスと,16 ビットのポート番号 (AF_INET)。 128 ビットの IP Version 6 アドレスと,16 ビットのポート番号 (AF_INET6)。
セキュリティ パス名と接続しているプロセスは,そのパスへの書き込みアクセスを持っていなければならない。 利用できない。
rawアクセス 利用できない。 特権プロセスは,IP の raw ファシリティにアクセスできる。 raw ソケットは,1 つの IP プロトコル番号に関連付けられ,そのプロトコル用に受信したすべてのトラフィックを受信する。

4.1.1.3    ソケット・タイプ

各ソケットには,抽象表現のタイプが関連付けられており,そのソケット・タイプを使用する通信の意味を記述します。 メッセージの信頼性,順序付け,重複の防止などのプロパティは,このソケット・タイプによって決定されます。 ソケット・タイプの基本的なセットは,<sys/socket.h> ヘッダ・ファイルに定義されています。

注意

通常,ヘッダ・ファイル名は山カッコ (< >) で囲まれています。 ヘッダ・ファイルへの絶対パスは,山カッコ内のヘッダ・ファイル名の前に /usr/include/ を付けたものです。 <sys/socket.h> の場合,socket.h/usr/include/sys ディレクトリにあります。

UNIX ドメインおよびインターネット・ドメインでは,次のソケット・タイプを使用できます。

SOCK_DGRAM

固定最大長のコネクションレス型メッセージであるデータグラムを提供する。

各メッセージは,個別にアドレスを指定できます。 このソケット・タイプでは,メッセージの引き渡し順序および信頼性が保証されないので,一般に短いメッセージに使用されます。 データグラム・ソケットの重要な特性は,データのレコード境界が保存されるので,個々のデータグラムを別々に読み取ることにあります。

データグラムは,finger プログラムのような受信側からの 1 つ以上の応答を必要とする要求に対してよく使用されます。 指定された時間内に受信側が応答しない場合,送信側アプリケーションは要求を繰り返すことができます。 この時間は,通信ドメインによって異なります。

UNIX ドメインでは,SOCK_DGRAM はメッセージ・キューに似ています。 インターネット・ドメインでは,SOCK_DGRAM はユーザ・データグラム・プロトコル (UDP) を使用してインプリメントされます。

SOCK_STREAM

帯域外データ用に,接続を通し,伝送機能を伴う連続した双方向バイト・ストリームを提供する。

データの伝送は,高い信頼性で,順番に行われます。

UNIX ドメインでは,SOCK_STREAM は,全二重パイプに似ています。 インターネット・ドメインでは,SOCK_STREAM は,伝送制御プロトコル (TCP) を使用してインプリメントされます。

SOCK_RAW

ネットワーク・プトコロルおよびインタフェースへのアクセスを提供する。

raw ソケットは,特権プロセスだけが利用できます。 raw ソケットを使用すると,アプリケーションは,下位レベルの通信プロトコルに直接アクセスできます。 raw ソケットは,通常のインタフェースを通しては直接アクセスできないプロトコル機能を使用したい上級ユーザ, または既存の下位レベルのプロトコルを使用して,新しいプロトコルを構築したい上級ユーザが使用することを目的としています。 また,SOCK_RAW は,ハードウェア・インタフェースとの通信にも使用できます。

raw ソケットは,通常,データグラム指向です。 ただし,厳密な特性は,プロトコルが提供するインタフェースに依存します。 raw ソケットは,インターネット・ドメインだけで利用できます。

4.1.1.4    ソケット名

ソケットに名前を付けることによって,システムまたはネットワーク上の関連のないプロセスが,特定のソケットを指定して,そのソケットとデータを交換することができます。 バインドされた名前は,可変長のバイト列であり,サポートしている 1 つまたは複数のプロトコルによって解釈されます。 名前の解釈は,通信ドメインによって異なります。 インターネット・ドメインでは,名前はインターネット・アドレスとポート番号から構成され,ファミリは AF_INET か AF_INET6 のいずれかです。 AF_INET ソケットは IPv4 通信をサポートし,AF_INET6 ソケットは IPv4 通信と IPv6 通信の両方をサポートします。 UNIX ドメインでは,名前はパス名であり,ファミリは AF_UNIX です。

互いに通信しているプロセスは,関連付け (association) によってバインドされます。 インターネット・ドメインでは,関連付けは,プロトコル,ローカル・アドレスと外部アドレス,およびローカル・ポートと外部ポートから構成されます。 インターネット・ドメインのソケットに名前をバインドすると,ローカル・アドレスおよびローカル・ポートが指定されます。

UNIX ドメインでは,関連付けは,ローカル・パス名から構成されます。 UNIX ドメインのソケットに名前をバインドすると,パス名が指定されます。

ほとんどのドメインにおいて,関連付けは一意でなければなりません。

4.2    アプリケーションとソケットとのインタフェース

カーネルにソケットをインプリメントすると,ネットワーキング・サブシステムは,次の 3 つの対話処理層に分かれます。

4.1.1 項で説明した抽象表現の他に,ソケット・インタフェースには,システム・コールとライブラリ・コール,ライブラリ関数,およびデータ構造体があります。 これらにより,ユーザは,ソケットを操作したり,データの送受信を行うことができます。

さらに,カーネルはソケット・フレームワークに対して補助サービスを提供します。 これには,バッファ管理,メッセージのルーティング,プロトコルへの標準インタフェース,およびさまざまなネットワーク・プロトコルを使用するためのネットワーク・インタフェース・ドライバへのインタフェースなどが含まれます。

4.2.1    通信モード

ソケット・フレームワークは,コネクション指向型およびコネクションレス型の両通信モードをサポートします。 コネクション指向型通信では,アプリケーションは,コネクション指向型プロトコルをサポートするソケット・タイプを通信ドメインに指定します。 たとえば,アプリケーションは,AF_INET ドメインで SOCK_STREAM ソケットをオープンできます。 AF_INET ドメインおよび AF_INET6 ドメインの SOCK_STREAM ソケットは TCP プロトコルによってサポートされ,これは,コネクション指向型プロトコルです。

コネクションレス型通信では,アプリケーションは,コネクションレス型プロトコルをサポートするソケット・タイプを通信ドメインに指定します。 たとえば,AF_INET 通信ドメインの SOCK_DGRAM ソケットは,UDP プロトコルによってサポートされ,これは,コネクションレス型プロトコルです。

4.2.1.1    コネクション指向型通信

TCP は,オペレーティング・システムでインプリメントされているコネクション指向型プロトコルです。 TCP は,信頼性の高い端末相互トランスポート・プロトコルであり,消失データ,伝送エラー,および介入するゲートウェイにおける障害からの回復機能を提供します。 TCP では,2 つのプロセスが通信前に接続されていることを要求するため,データが正確に引き渡されることが保証されます。 TCP/IP 接続は,電話回線接続とよく比較されます。 AF_INET もしくは AF_INET6 ドメインの SOCK_STREAM ソケットを介して送信されるデータは,セグメントに分けられ,連番によって識別されます。 リモート・プロセスは,肯定応答に連番を含めることによって,データの受信を確認します。 データが途中で失われた場合には,再送信されるため,データは確実に正しい順序でアプリケーションに届きます。

大量のデータを交換し,データの到着順序が重要なアプリケーションでは,コネクション指向型通信が適しています。 ファイル転送プログラムは,TCP によって提供されるコネクション指向モードの通信を利用するアプリケーションの良い例です。

4.2.1.2    コネクションレス型通信

UDP は,オペレーティング・システムでインプリメントされているコネクションレス型プロトコルです。 UDP の機能を次に示します。

UDP メッセージは,失われたり,重複したり,または順序どおりに到着しないことがあります。

交換するデータ量が少なく,到着順序が重要でない場合は,コネクションレス型通信が適しています。 コネクションレス・モードの通信を使用した例の 1 つに,rwhod デーモンがあります。 この rwhod デーモンは,システム情報を含む UDP パケットを定期的にネットワークにブロードキャストします。 rwhod デーモンでは,パケットが送信されたかどうかや,送信順序はあまり重要ではありません。

UDP は,IP マルチキャストを使用して,データグラムをローカル・エリア・ネットワーク上のホストのサブセットに引き渡すアプリケーションにも適しています。

4.2.2    クライアント/サーバ・モデル

分散型アプリケーションの作成において最も一般に使用されるモデルは,クライアント/サーバ・モデルです。 サーバ・プロセスは,ネットワークに対してサービスを提供し,クライアント・プロセスは,これらのサービスを使用します。 クライアントとサーバが,サービスを提供して,受け入れるためには,周知の規約を定めておく必要があります。 この規約とは,プロトコルを接続の両端にインプリメントしなければならないということです。 状況に応じて,このプロトコルは,コネクション指向型 (非対称),またはコネクションレス型 (対称) のいずれかにすることができます。

TCP などのコネクション指向型プロトコルにおいては,通信の片側は常にサーバとして認識され,もう片側はクライアントとして認識されます。 サーバは,サービスに関連付けられた周知のアドレスにソケットをバインドし,次に,そのソケット上でパッシブにリッスンします。 クライアントは,サーバのソケットに対する接続を開始することによって,サーバからのサービスを要求します。 サーバが接続を受け入れると,サーバとクライアントはデータを交換できます。 コネクション指向型プロトコルの例として,Telnet があります。

UDP などのコネクションレス型プロトコルにおいては,通信のどちら側も,サーバまたはクライアントの役割を果たすことができます。 クライアントは,サーバとの接続を確立しません。 代わりに,データグラムをサーバのアドレスに送信するだけです。 同様に,サーバも,クライアントからの接続を受け入れません。 代わりに,recvfrom システム・コールを発行して,クライアントからのデータが到着するまで待ちます (4.3.6 項参照)。

4.2.3    システム・コール,ライブラリ・コール,ヘッダ・ファイル,およびデータ構造体

この項では,ソケット層に属するシステム・コールおよびライブラリ・コールの一覧を示します。 また,ソケットに関連する定数と構造体を定義するヘッダ・ファイルの一覧も示して,これらのヘッダ・ファイルに記述されている最も重要なデータ構造体について説明します。

4.2.3.1    ソケット・システム・コール

表 4-2 は,ソケット・システム・コールと,その機能の簡単な説明の一覧です。 各システム・コールには,同じ名前の関連するリファレンス・ページがあります。

表 4-2:  ソケット・システム・コール

システム・コール 説明
accept 新しいソケットを作成するために,ソケット上で接続を受け入れる。
bind 名前をソケットにバインドする。
connect ソケットで接続を開始する。
getpeername 接続された対等プロセスの名前を取得する。
getsockname ソケット名を取得する。
getsockopt ソケットのオプションを取得する。
listen ソケットの接続をリッスンし,キューにいれる要求の最大数を指定する。
recv メッセージを受信し,着信データの中身を見て,帯域外データを受信する。
recvfrom メッセージを受信する。 recv システム・コールのすべての機能を行うほか,対等プロセスのアドレスを提供する。
recvmsg メッセージを受信する。 recv および recvfrom システム・コールのすべての機能を行うほか,特殊に解釈されたデータ (アクセス権) を受信し,メッセージ・バッファに対して分散 I/O オペレーションを行う。
send メッセージを送信する。 ネットワーク・ルーティングを指定せずに,帯域外データおよび通常データも送信する。
sendmsg メッセージを送信する。 send および sendto システム・コールのすべての機能を行うほか,特殊に解釈されたデータ (アクセス権) を伝送し,メッセージ・バッファに対して収集 I/O オペレーションを行う。
sendto メッセージを送信する。 send システム・コールのすべての機能を行うほか,対等プロセスのアドレスを提供する。
setsockopt ソケット・オプションを設定する。
shutdown すべてのソケット送受信オペレーションをシャットダウンする。
socket 通信用の終端を作成し,記述子を返す。
socketpair 接続した 1 組のソケットを作成する。

4.2.3.2    ソケット・ライブラリ・コール

アプリケーション・プログラムは,分散環境におけるプロセス間通信機能を使用するために,ソケット・ライブラリ・コールを使用して,ネットワーク・アドレスを構築します。

ネットワーク・ライブラリ・サブルーチンは,次の項目をマップします。

名前およびアドレスの操作を簡単にするために,追加のソケット・ライブラリ・コールが用意されています。

ソケット・ライブラリ・コールを使用する場合は,アプリケーション・プログラムに,<netdb.h> ヘッダ・ファイルをインクルードしなければなりません。

ホスト名

アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,インターネット・ホスト名をアドレスにマップします。

gethostbyname ルーチンは,インターネット・ホスト名を受け取り,hostent 構造体を返します。 一方,gethostbyaddr ルーチンは,インターネット・ホスト・アドレスを hostent 構造体にマップします。 hostent 構造体は,次の構成要素からなります。

struct hostent {
   char *h_name;        /* official name of host */
   char **h_aliases;    /* alias list */
   int  h_addrtype;     /* host address type (AF_INET or AF_INET6) */
   int  h_length;       /* length of address */
   char **h_addr_list;  /* list of addresses, null terminated
                           first address, network byte order */
#define h_addr h_addr_list[0]
};

gethostbyaddr サブルーチンおよび gethostbyname サブルーチンは,ホストの公式名と公用の別名を返します。 また,アドレス・ファミリおよび可変長アドレスのヌル終了リストも同時に返されます。 このアドレスのリストが必要なのは,ホストが,同じ名前で多くのアドレスを持つ可能性があるからです。

これらのライブラリ・コールのデータベースは,/etc/hosts ファイルです。 named ネーム・サーバが実行中の場合は,ホスト・データベースはネットワーク上の指定サーバ上で保持されます。 データベース間やそのアクセス・プロトコル間の相違のために,返される情報が異なることがあります。 /etc/hosts バージョンの gethostbyname を使用している場合は,1 つのアドレスだけが返されますが,リストされている別名はすべて含まれます。 named バージョンの場合には,代替アドレスを返すことができますが,パラメータ値として指定されたもの以外の別名は返しません。

getaddrinfo ルーチンは,インターネット・ノード名またはサービス名を受け取り,1 つ以上の addrinfo 構造体を返します。 一方,getnameinfo ルーチンは,sockaddr を受け取り,ホスト名 (要求された場合) と,そのポートのサービス名 (要求された場合) を返します。

これらのコールのデータベースは,/etc/ipnodes ファイルと /etc/hosts ファイルの両方です。 named ネーム・サーバが実行中の場合は,ホスト・データベースはネットワーク上の指定サーバ上で保持されます。 データベース間やそのアクセス・プロトコル間の相違のために,返される情報が異なることがあります。 /etc/ipnodes および /etc/hosts バージョンの getaddrinfo を使用している場合は,要求に応じて,2 つのアドレス (各ファイルから 1 つづつ) が返されることがあります。 別名は返されません。 named バージョンの場合には,代替アドレスを返すことができますが,パラメータ値として指定されたもの以外の別名は返しません。

ネットワーク名

アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,ネットワーク名をネットワーク番号に,ネットワーク番号をネットワーク名にマップします。

getnetbyaddrgetnetbyname,および getnetent の各ルーチンは,/etc/networks ファイルから情報を取り出し,次のような netent 構造体を返します。

struct netent {
   char           *n_name;     /* official name of net */
   char           **n_aliases; /* alias list */
   int            n_addrtype;  /* net address type */
   in_addr_t      n_net;       /* network number, host byte order */
};
 

プロトコル名

アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,プロトコル名をプロトコル番号にマップします。

getprotobynumbergetprotobyname,および getprotoent の各サブルーチンは, /etc/protocols ファイルから情報を取り出し,次のような protoent エントリを返します。

struct protoent {
   char *p_name;     /* official protocol name */
   char **p_aliases; /* alias list */
   int  p_proto;     /* protocol number */
};
 

サービス名

アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,サービス名をポート番号にマップします。

サービスは,特定のポートに常駐し,特定の通信プロトコルを使用するものとされています。 この考え方は,インターネット・ドメインでは一貫していますが,他のネットワーク・アーキテクチャでは一貫していません。 さらに,サービスは,複数のポートに常駐することがあります。 この場合には,上位レベルのライブラリ・ルーチンを迂回するか,または拡張しなければなりません。 利用できるサービスは,/etc/services ファイルに入っています。 サービス・マッピングは,次のような servent 構造体によって記述されます。

struct servent {
   char *s_name;     /* official service name */
   char **s_aliases; /* alias list */
   int  s_port;      /* port number, network byte order */
   char *s_proto;    /* protocol to use */
};
 

getservbyname ルーチンは,サービス名,および修飾プロトコル (オプション) を指定することにより,サービス名を servent 構造体にマップします。 したがって,次のライブラリ・コールは,任意のプロトコルを使用する Telnet サーバのサービス仕様を返します。

sp = getservbyname("telnet", (char *) NULL);
 

一方,次のライブラリ・コールは,TCP プロトコルを使用する Telnet サーバだけを返します。

sp = getservbyname("telnet", "tcp");
 

getservbyport ルーチンと getservent ルーチンも提供されています。 getservbyport ルーチンには,getservbyname が提供するインタフェースと類似したインタフェースがあり,必要に応じてプロトコル名を指定することにより,限定して検索することができます。

ネットワーク・バイト順の変換

プログラムでインターネット・プロトコル (IP) 群のデータを作成または解釈する必要がある場合には,標準の変換方法を使用します。 IP 群では,特定のデータ・フォーマットを使用することによって,一貫性を保証します。 オペレーティング・システムでは,プログラムがそのフォーマットとの間でデータ変換を行うことができるようにする関数を提供しています。 また,インターネット・プロトコル群では,最上位バイトが最下位アドレスにあると想定します。 このようなフォーマットを big-endian と呼びます。 ネットワーク・バイト順とホスト・バイト順を相互に変換する関数が用意されています。

次の 4 つの関数によって,ネットワークが,ユーザのプログラムから渡されるデータを正しく解釈し,また,ユーザのプログラムが,ネットワークから渡されるデータを解釈することが保証されます。

アプリケーション・プログラムは,次の互いに関連するネットワーク・ライブラリ・ルーチンを使用して,インターネット・アドレス文字列とバイナリ・アドレスの操作を行います。

表 4-3 に,ソケット・ライブラリ・コールの一覧と,簡単な説明を示します。 各ライブラリ・コールには,同じ名前の関連するリファレンス・ページがあります。 ソケット・ライブラリ・コールは libc の一部であるため,特別なライブラリをリンクする必要はありません。

表 4-3:  ソケット・ライブラリ・コール

名前 説明
endhostent 一連のホスト・エントリ検索を終了する。
endnetent 一連のネットワーク・エントリ検索を終了する。
endprotoent 一連のプロトコル・エントリ検索を終了する。
endservent 一連のサービス・エントリ検索を終了する。
freeaddrinfo getaddrinfo から返された addrinfo 構造体と記憶域を解放する。
getaddrinfo

ホストの名前とオプションのアドレス・ファミリが指定されると,ネーム・サーバ (named),/etc/ipnodes ファイル,または /etc/hosts ファイルのいずれかから,そのホスト・エントリを検索する。

標準の数値文字列フォーマットのノード・アドレスを,sockaddr 構造体のインターネット・アドレスに変換する。

gethostbyaddr ホストのアドレスが指定されると,ネーム・サーバ (named) または /etc/hosts ファイルのいずれかから,そのホスト・エントリを検索する。
gethostbyname ホスト名が指定されると,ネーム・サーバ (named) または /etc/hosts ファイルのいずれかから,そのホスト・エントリを検索する。
gethostent ネーム・サーバ (named),または /etc/hosts ファイル (必要に応じてこのファイルをオープンする) から,次のホスト・エントリを検索する。
getnameinfo

ノードのアドレスを含む sockaddr 構造体が指定されると,ネーム・サーバ (named),/etc/ipnodes ファイル,または /etc/hosts ファイルのいずれかから,そのホスト・エントリを検索する。

sockaddr 構造体のインターネット・アドレスを,標準の数値文字列フォーマットに変換する。

getnetbyaddr ネットワークのアドレスが指定されると,そのネットワーク・エントリを /etc/networks ファイルから検索する。
getnetbyname ネットワーク名が指定されると,そのネットワーク・エントリを /etc/networks ファイルから検索する。
getnetent /etc/networks ファイル (必要に応じてこのファイルをオープンする) から,次のネットワーク・エントリを検索する。
getprotobyname プロトコル名が指定されると,そのプロトコル・エントリを /etc/protocols ファイルから検索する。
getprotobynumber プロトコル番号が指定されると,そのプロトコル・エントリを /etc/protocols ファイルから検索する。
getprotoent /etc/protocols ファイル (必要に応じてこのファイルをオープンする) から,次のプロトコル・エントリを検索する。
getservbyname サービスの名前が指定されると,そのサービス・エントリを /etc/services ファイルから検索する。
getservbyport サービスのポート番号が指定されると,そのサービス・エントリを /etc/services ファイルから検索する。
getservent /etc/services ファイル (必要に応じてこのファイルをオープンする) から,次のサービス・エントリを検索する。
htonl 32 ビット整数を,ホストのバイト順から,インターネット・ネットワークのバイト順に変換する。
htons 符号なしの短整数を,ホストのバイト順から,インターネット・ネットワークのバイト順に変換する。
inet_addr インターネットの標準ドット (.) 表記で表現された番号を表す文字列を分割して,インターネット・アドレスを返す。
inet_lnaof インターネット・ホスト・アドレスを分割して,ローカル・ネットワーク・アドレスを返す。
inet_makeaddr インターネット・ネットワーク番号およびローカル・ネットワーク・アドレスから,インターネット・アドレスを構築する。
inet_ntoa インターネット・アドレスを文字列に変換する。
inet_netof インターネット・ホスト・アドレスを分割して,ネットワーク番号を返す。
inet_network インターネットの標準ドット (.) 表記で表現された番号を表す文字列を分割して,インターネット・ネットワーク番号を返す。
ntohl 32 ビット整数を,インターネット・ネットワーク標準バイト順から,ホストのバイト順に変換する。
ntohs 符号なしの短整数を,インターネット・ネットワークのバイト順から,ホストのバイト順に変換する。
sethostent 一連のホスト・エントリ検索を開始する。
setnetent 一連のネットワーク・エントリ検索を開始する。
setprotoent 一連のプロトコル・エントリ検索を開始する。
setservent 一連のサービス・エントリ検索を開始する。

4.2.3.3    ヘッダ・ファイル

ソケット・ヘッダ・ファイルには,データ定義,構造体,定数,マクロ,およびオプションが記述されており,ソケット・システム・コールおよびサブルーチンがこれらを使用します。 アプリケーション・プログラムでは,特定のシステム・コールまたはサブルーチンに必要な構造体,およびその他の情報を使用するために, 適切なヘッダ・ファイルをインクルードしなければなりません。 表 4-4 は,一般に使用されるソケット・ヘッダ・ファイルの一覧です。

表 4-4:  ソケット・インタフェースのヘッダ・ファイル

ファイル名 説明
<sys/socket.h> データ定義およびソケット構造体が記述されている。 このファイルは,すべてのソケット・アプリケーションにインクルードする必要がある。
<sys/types.h> データ型定義が記述されている。 このファイルは,すべてのソケット・アプリケーションにインクルードする必要がある。 このヘッダ・ファイルは <sys/socket.h> でインクルードされている。
<sys/un.h> UNIX ドメイン用の構造体を定義する。 UNIX ドメインのソケットを使用する場合には,アプリケーションにこのファイルをインクルードする必要がある。
<netinet/in.h> インターネット・ドメイン用の定数と構造体を定義する。 インターネット・ドメインで TCP/IP を使用する場合には,アプリケーションにこのファイルをインクルードする必要がある。
<netdb.h> ソケット・サブルーチン用のデータ定義が記述されている。 TCP/IP を使用し,ホスト・エントリ,ネットワーク・エントリ,プロトコル・エントリ,サービス・エントリのいずれかを検索する必要がある場合は,アプリケーションにこのファイルをインクルードする必要がある。

4.2.3.4    ソケットに関連するデータ構造体

この項では,次のデータ構造体について説明します。

sockaddr 構造体には,ソケットのアドレス・フォーマットに関する情報が入っています。 アプリケーションがソケットを作成する通信ドメインは,そのソケットのアドレス・フォーマットを決定し,そのデータ構造体も決定します。

ソケット・アドレスのデータ構造体は,4.2.3.3 項で説明しているヘッダ・ファイルに定義されています。 どのヘッダ・ファイルが適切であるかは,作成するソケットのタイプによって異なります。 使用可能なソケット・アドレスのデータ構造体の型は,次のとおりです。

struct sockaddr

汎用的なソケット・アドレス構造体を定義する。

このソケットは,14 バイトの直接アドレッシングに制限されています。 sockaddr 構造体は <sys/socket.h> ファイルに記述され,次の要素が含まれます。


unsigned char   sa_len;         /* total length */
sa_family_t     sa_family;      /* address family */
char            sa_data[14];    /* actually longer;
                                   address value */
 

sa_len パラメータは,合計長さを定義します。 sa_family パラメータは,ソケット・アドレス・ファミリまたはドメインを定義します。 UNIX ドメインには AF_UNIX,インターネット・ドメインでは AF_INET もしくは AF_INET6 です。 sa_data の内容は,使用しているプロトコルによって異なりますが,一般に,ソケット名は,マシン名,およびポート名またはサービス名から構成されます。

struct sockaddr_storage

ネットワークを介したマシン間通信およびローカルなプロセス間通信に使用されるインターネット・ドメイン・ソケット (AF_INET および AF_INET6 アドレス・ファミリ) を定義する。 この構造体を使用すると,アプリケーションは複数のアドレス・ファミリを 1 つの変数として扱うことができます。 sockaddr_storage 構造体は,<sys/socket.h> ファイルに記述されています。 sockaddr_storage 構造体には,次の要素が含まれます。

unsigned char  ss_len;     /* address length */
sa_family_t    ss_family;  /* address family */
char           __ss_pad1[_SS_PAD1SIZE];  /* pad to alignment field */
ulong_t        __ss_align;               /* force structure alignment */
char           __ss_pad2[_SS_PAD2SIZE];  /* pad to desired size */

_SS_PAD1SIZE 変数と _SS_PAD2SIZE 変数も,<sys/socket.h> で定義されています。

struct sockaddr_un

同一マシン上におけるプロセス間通信に使用する,UNIX ドメインのソケットを定義する。

このソケットには,絶対パス名の指定が必要です。 sockaddr_un 構造体は <sys/un.h> ヘッダ・ファイルに記述されています。 sockaddr_un 構造体には,次の要素が含まれます。

unsigned char sun_len;    /* sockaddr len including null*/
sa_family_t   sun_family; /* AF_UNIX, address family*/
char          sun_path[]; /* path name */
 

UNIX ドメインのプロトコル (AF_UNIX) は,最大で PATH_MAX に 2 バイトを加えた長さのソケット・アドレスを持ちます。 PATH_MAX パラメータは,パス名の最大バイト数を定義します。

struct sockaddr_in

ネットワークを通したマシン間通信,およびローカルなプロセス間通信に使用する,インターネット・ドメインのソケット (AF_INET アドレス・ファミリ) を定義する。

sockaddr_in 構造体は <netinet/in.h> ファイルに記述されています。 sockaddr_in 構造体には,次の要素が含まれます。

unsigned char   sin_len;
sa_family_t     sin_family;
in_port_t       sin_port;
struct  in_addr sin_addr;
 

struct sockaddr_in6

ネットワークを通したマシン間通信,およびローカルなプロセス間通信に使用する,インターネット・ドメイン (AF_INET6 アドレス・ファミリ) のソケットを定義する。

sockaddr_in6 構造体は <netinet/in.h> ファイルに記述されています。 sockaddr_in6 構造体には,次の要素が含まれます。

uint8_t         sin6_len;
sa_family_t     sin6_family;
in_port_t       sin6_port;
uint32_t        sin6_flowinfo
struct in6_addr sin6_addr;
uint32_t        sin6_scope_id
 

in6_addr 構造体には,ネットワーク・バイト順のアドレスを 16 個の 8 ビットの要素の配列として格納されます。

次のデータ構造体を使うと,アプリケーションは sendmsg システム・コールと recvmsg システム・コールを使って,補助データの送信や受信を行うことができます。

struct msghdr

アプリケーションは,sendmsg および recvmsg の各システム・コールを使用して,システムが保持するオブジェクト (ファイル,デバイス,ソケットなど) に対するアクセス権を引き渡すことができます。 sendmsg および recvmsg の各システム・コールについての詳細は,4.3.6 項を参照してください。 データを伝送するプロセスは,UNIX ドメインのソケットに接続しなければなりません。

このデータ構造体 (<sys/socket.h> ヘッダ・ファイルに定義されています) により,AF_INET ソケットと raw AF_INET6 ソケットは,ある種のデータを受け取ることもできます。 IP_RECVDSTADDR オプションと IP_RECVOPTS オプション (IPv4 の場合),IPV6_RECVHOPOPTS オプション,IPV6_RECVDSTOPTS オプション,および IPV6_RECVRTHDR オプション (IPv6 の場合) の説明については, ip(7) を参照してください。

msghdr データ構造体には,次の要素が含まれます。

struct msghdr {
        void           *msg_name;       /* optional address */
        size_t          msg_namelen;    /* size of address */
        struct iovec   *msg_iov;        /* scatter/gather array */
        int             msg_iovlen;     /* # elements in msg_iov */
        void           *msg_control;    /* ancillary data, see below */
        size_t          msg_controllen; /* ancillary data buffer len */
        int             msg_flags;      /* flags on received message */
};
 

XNS4.0 の msghdr データ構造体に加えて,オペレーティング・システムは,4.3BSD,4.4BSD,および POSIX 1003.1g Draft 6.6 の msghdr データ構造体もサポートしています。 BSD の msghdr データ構造体については,4.5 節で詳細に説明しています。

struct cmsghdr

sendmsg システム・コールと recvmsg システム・コールで転送される補助データ・オブジェクトの構造体です。 msghdr データ構造体の msg_control メンバは,cmsghdr 構造体に含まれる補助データを指しています。

一般的に,データ・オブジェクトは 1 個だけ cmsghdr 構造体で渡されます。 ただし,IPv6 Advanced Sockets API を使うと,sendmsg システム・コールと recvmsg システム・コールで複数のオブジェクトを渡せるようになります。 raw IPv6 ソケットについては,4.7.2 項を参照してください。 このデータ構造体は,<sys/socket.h> ヘッダ・ファイルに定義されています。

cmsghdr データ構造体は,次の構成要素からなっています。

struct cmsghdr {
        socklen_t       cmsg_len;       /* #bytes, including this header */
        int             cmsg_level;     /* originating protocol */
        int             cmsg_type;      /* protocol-specific type */
                        /* followed by unsigned char cmsg_data[]; */
};

4.3    ソケットの使用方法

この節では,ソケットの作成と使用に必要な手順の概略を説明します。 次の手順は,コネクション指向型およびコネクションレス型の通信モードについて説明しています。

4.3.1    ソケットの作成

ソケットを使用する最初の手順は,ソケットの作成です。 ソケットは,socket または socketpair のシステム・コールを使用して,オープン,つまり作成します。

socket システム・コールは,ソケット記述子 s を返します。 これは,アプリケーション・プログラムが,新しく作成されたソケットを後続のシステム・コールで参照するときに使用する,非負の整数です。 返されるソケット記述子は,呼び出したプロセスでそのような記述子に使用できる値のうち,未使用の最小値であり,カーネル記述子テーブルへのインデックスになります。

関数の構文,パラメータ,エラーについては socket(2) を参照してください。

たとえば,AF_INET6 アドレス・ファミリを使用してインターネット・ドメインでストリーム・ソケットを作成するには,次の呼び出しを使用できます。

if ((s = socket(AF_INET, SOCK_STREAM,0)) == -1 ) {
         fprintf(file1,"socket() failed\n");
         local_flag = FAILED;
     }
 

このように呼び出すと,基礎となる通信が TCP プロトコルをサポートしている場合には,そのプロトコルを使用してストリーム・ソケットが作成されます。 UNIX ドメインでデータグラム・ソケットを作成するには,次の呼び出しを使用できます。

if ((s = socket(AF_UNIX, SOCK_DGRAM,0)) == -1 ) {
         fprintf(file1, "socket() failed\n");
         local_flag = FAILED;
     }
 

このように呼び出すと,基礎となる通信が UNIX ドメインのプロトコルをサポートしている場合には,そのプロトコルを使用してデータグラム・ソケットが作成されます。

また,socketpair システム・コールも,ソケットの作成に使用できます。 socketpair システム・コールは,すでに接続されている,名前のない 1 組のソケットを作成します。

socketpair システム・コールは,1 組のソケット記述子を返します。 これは,アプリケーションが,新しく作成された 1 組のソケットを後続のシステム・コールで参照するときに使用する,非負の整数です。

関数の構文,パラメータ,エラーについては, socketpair(2) を参照してください。

次の例は,1 組のソケットを作成する方法を示しています。

{

.
.
.
int sv[2];
.
.
.
if ((s = socketpair (AF_UNIX, SOCK_STREAM, 0, sv)) < 0) { local_flag=FAILED; fprintf(file1, "socketpair() failed\n"); }
.
.
.
}  

4.3.1.1    実行モードの設定

ソケットは,ブロッキングまたは非ブロッキングのいずれかの I/O モードに設定できます。 fcntl オペレーションの O_NONBLOCK が,このモードの決定に使用されます。 省略時には,O_NONBLOCK はクリアされており (セットされない),ソケットはブロッキング・モードになります。 ブロッキング・モードでは,ソケットは,read を実行しようとしてデータが利用できなかった場合,データが利用できるようになるまで待ちます。

O_NONBLOCK がセットされている場合,ソケットは非ブロッキング・モードです。 非ブロッキング・モードでは,呼び出したプロセスが read を実行しようとしてデータが利用できなかった場合, そのソケットは即座に EWOULDBLOCK エラー・コードを指定して戻ります。 データが利用できるようになるのを待ちません。 同様に,書き込み処理においては,ソケットに O_NONBLOCK がセットされていると,出力キューが一杯の場合,ソケットが write を実行しようとすると,プロセスは EWOULDBLOCK エラー・コードを指定して即座に戻ります。

次の例は,ソケットを非ブロッキングにマークする方法を示しています。

#include <fcntl.h>

.
.
.
int s;
.
.
.
if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) perror("fcntl F_SETFL, O_NONBLOCK"); exit(1); }
.
.
.

ソケットで非ブロッキング I/O を行う場合,プログラムは EWOULDBLOCK エラーをチェックしなければなりません。 このエラーはグローバル変数 errno に格納されます。 オペレーションが通常にブロックしても,実行していたソケットが非ブロッキングと設定されている場合には,EWOULDBLOCK エラーが生じます。 次のソケット・システム・コールはすべて,EWOULDBLOCK エラー・コードを返します。

これらのシステム・コールを非ブロッキングのソケットで使用するプロセスは,EWOULDBLOCK リターン・コードを処理する準備をしておかなければなりません。

send などのオペレーションが完了できなくても,一部の書き込みが可能な場合 (たとえば,SOCK_STREAM ソケットを使用している場合) には,ただちに送信できるデータが処理され,リターン値は,実際に送信されたデータの量を示します。

4.3.2    名前とアドレスのバインド

bind システム・コールはアドレスをソケットに関連付けます。 ソケットのドメインは socket システム・コールを使用して設定します。 bind システム・コールを使用すると,それが使用されるドメインに関係なく,ローカル・プロセスは自身の情報,たとえばローカル・ポートやローカル・パス名などを書き込むことができます。 この情報によって,サーバ・アプリケーションをクライアント・アプリケーションから呼び出すことができます。

次の例は,AF_INET アドレス・ファミリのインターネット・ドメインで作成された SOCK_STREAM ソケットで,bind システム・コールを使用する方法を示しています。

#define PORT 3000
 
int     retval;           /* General return value */
int     s1_descr;         /* Socket 1 descriptor */

.
.
.
struct sockaddr_in sock1addr; /* Address struct for socket1.*/
.
.
.
s1_descr = socket (AF_INET, SOCK_STREAM, 0); if (s1_descr < 0) /* Call failed */
.
.
.
bzero(&sock1addr, sizeof(sock1addr)); sock1addr.sin_family = AF_INET; sock1addr.sin_addr.s_addr = INADDR_ANY; sock1addr.sin_port = htons(PORT); retval = bind (s1_descr, &sock1addr, sizeof(sock1addr)); if (retval < 0) /* Call failed */
.
.
.

関数の構文,パラメータ,エラーについては, bind(2) を参照してください。 名前とアドレスのバインドの詳細については,4.7.4 項 を参照してください。

4.3.3    接続の確立

ソケットは,接続されていない状態で作成されます。 クライアント・プロセスは,connect システム・コールを使用して,サーバ・プロセスに接続するか,またはサーバのアドレスをローカルに格納します。 これは,通信が,コネクション指向型かコネクションレス型かによって異なります。 インターネット・ドメインでは,一般に,connect システム・コールによって,関連付けのローカル・アドレス,ローカル・ポート,外部アドレス,および外部ポートが割り当てられます。

connect システム・コールの構文は,通信ドメインによって異なります。

接続に失敗した場合には,エラーが返されます。 ただし,システムによって自動的にバインドされた名前はすべて残ります。 アプリケーションは,close システム・コールを使用して,ソケットおよび記述子の割り当てを解除します。 4.6 節表 4-6 に,ソケットに関連する一般的なエラーをリストします。 接続が正常に行われた場合,ソケットはサーバに関連付けられ,データ転送が開始されます。

関数の構文,パラメータ,エラーについては, connect(2) を参照してください。

インターネット・ドメインでコネクション指向型プロトコルを選択すると,TCP が選択されます。 この場合,connect システム・コールは,デスティネーションとの TCP 接続を確立するか,または確立できない場合はエラーを返します。 TCP を使用するクライアント・プロセスは,connect システム・コールを呼び出して接続を確立しておかなければなりません。 これはストリーム・ソケット (SOCK_STREAM) を通してデータ転送の信頼性を高めるためです。

インターネット・ドメインでコネクションレス型プロトコルを選択すると,UDP が選択されます。 コネクションレス型プロトコルを使用するクライアント・プロセスは,使用前に接続する必要はありません。 このような環境で connect を使用すると,デスティネーション (つまりサーバ) のアドレスがローカルに格納されるので,クライアント・プロセスは,メッセージを送信するたびにサーバのアドレスを指定する必要がありません。 このソケットで送信されるすべてのデータは,接続されたサーバ・プロセスに自動的にアドレス指定され,そのサーバ・プロセスから受信したデータだけが引き渡されます。

接続するアドレスは,各ソケットに対して常に 1 つしか指定できません。 2 回目の connect システム・コールでは,デスティネーション・アドレスが変更され,空アドレス (たとえば,AF_NET アドレス INADDR_ANY) への connect システム・コールを呼び出すと,切断されます。 コネクションレス型プロトコルにおける connect システム・コールは,ただちに戻ります。 これは,オペレーティング・システムがサーバのソケットのアドレスを記録するだけだからです。 コネクション指向型プロトコルの場合,接続要求は端末相互接続の確立を開始するので,コネクションレス型プロトコルはこの点で異なります。

コネクションレス型プロトコルを使用するソケットが接続されると,最後に呼び出した send システム・コールからのエラーが,同期をはずれて戻ることがあります。 このようなエラーは,後でそのソケットに対して続いて行うオペレーションで報告されます。 特殊ソケット・オプション SO_ERROR (getsockopt システム・コールで使用する) を使用して,エラー状態を照会することもできます。 次のデータをいつ送受信できるかを判断するために,select システム・コールを発行すると,このシステム・コールは,プロセスがエラー表示を受信した場合,真の値を返します。

どんな場合でも,次のオペレーションはエラーを返し,エラー状態をクリアします。

関数の構文,パラメータ,エラーについては, select(2) を参照してください。

次は,select システム・コールの例です。

if ( (ret_val = select(20,&read_mask,NULL,NULL,&tp)) != i )
 

4.3.4    接続の受け入れ

コネクション指向型のサーバ・プロセスは,通常,周知のアドレスでサービス要求をリッスンします。 つまり,サーバ・プロセスは,クライアントをサーバ・アドレスに接続することによって接続が要求されるまで,休止状態のままです。 接続が要求されると,サーバ・プロセスはウェイクアップし,クライアントが要求するアクションを実行してクライアントに対してサービスを行います。

コネクション指向型サーバは,listen および accept のシステム・コールを使用して,クライアント・プロセスとの接続に対して準備し,接続を受け入れます。

listen システム・コールは,通常,socket および bind のシステム・コールの後で呼び出されます。 このシステム・コールは,サーバがクライアントから接続要求を受信する準備ができていることを示します。

関数の構文,パラメータ,エラーについては, listen(2) を参照してください。

サーバは,accept システム・コールを使用することによって,クライアントとの接続を受け入れます。

accept システム・コールは,クライアントがサービスを要求するまで,サーバをブロックします。 このシステム・コールは,呼び出し中に SIGCHLD などのシグナルによって割り込まれると,障害状態を返します。 したがって,accept からのリターン値は,接続が確立されたことを保証するためにチェックされます。

関数の構文,パラメータ,エラーについては, accept(2) を参照してください。

接続が確立されると,サーバは通常,子プロセスをフォークします。 これにより,リッスンしているソケットと同じプロパティを持つ,別のソケットが作成されます。 次の例は,親プロセスが接続要求をキューにいれるために使用するソケット s が子プロセスでクローズされ,accept システム・コールで作成されたソケット g が親プロセスでクローズされる方法を示しています。 クライアントのアドレスも,doit ルーチンに渡されます。 これは,クライアントを認証するために必要だからです。 accept システム・コールが新しいソケットを作成した後は,元のソケット上でリッスンを継続しながら,新しいソケットで,クライアントの接続要求にサービスできます。 次に例を示します。

for (;;) {
   int g, len = sizeof (from);
 
   g = accept(s, (struct sockaddr *)&from, &len);
   if (g < 0) {
      if (errno != EINTR)
         syslog(LOG_ERR, "rlogind: accept: %m");
      continue;
   }
   if (fork() == 0) {   /* Child */
      close(s);
      doit(g, &from);
   }
   close(g);            /* Parent */
}
 

コネクションレス型サーバは,bind システム・コールを使用しますが,このサーバは,accept システム・コールではなく,recvfrom システム・コールを使用するため,クライアントの要求を待つことになります。 データ交換中には,コネクションレス型サーバとクライアントの間に接続は確立されません。

4.3.5    ソケット・オプションの設定と取得

ローカル・アドレスへのソケットのバインドやデスティネーション・アドレスへのソケットの接続の他に,アプリケーション・プログラムは,ソケットの制御ができなければなりません。 たとえば,タイム・アウトおよび再伝送を使用するプロトコルの場合には,アプリケーション・プログラムで,タイム・アウト・パラメータを取得または設定しなければならないことがあります。 また,バッファ・スペースの割り当ての制御,ソケットでのブロードキャストの伝送を可能にするかどうかの決定,あるいは帯域外データの処理の制御が必要な場合もあります。

getsockopt および setsockopt のシステム・コールによって,アプリケーション・プログラムでソケットのオペレーションを制御することができます。 setsockopt システム・コールを使用すると,アプリケーション・プログラムは,ソケット・オプションを設定することができます。 このとき使用する値のセットは,getsockopt システム・コールで取得する値のセットと同じです。

関数の構文,パラメータ,エラーについては, setsockopt(2) を参照してください。

次の例は,インターネット通信ドメインにおいて,ソケットに SO_SNDBUF オプションを設定する方法を示しています。

# include       <sys/socket.h>

.
.
.
int retval; /* General return value. */ int s1_descr; /* Socket 1 descriptor */ int sockbufsize=16384;
.
.
.
retval = setsockopt (s1_descr, SOL_SOCKET, SO_SNDBUF, (void *) &sockbufsize, sizeof(sockbufsize));  

getsockopt システム・コールを使用すると,アプリケーション・プログラムは,setsockopt システム・コールで設定されたソケット・オプションに関する情報を要求できます。

関数の構文,パラメータ,エラーについては, getsockopt(2) を参照してください。

次の例は,getsockopt システム・コールを使用して,既存のソケット上の SO_SNDBUF のサイズを判断する方法を示しています。

#include <sys/socket.h>

.
.
.
int retval; /* General return value. */ int s1_descr; /* Socket 1 descriptor */ int sbufsize; int len = sizeof(sbufsize);
.
.
.
retval = getsockopt (s1_descr, SOL_SOCKET, SO_SNDBUF, (void *)&sbufsize, &len);  

SOL_SOCKET パラメータは,汎用ソケット・レベル・コードで SO_SNDBUF パラメータが解釈されることを示します。 SO_SNDBUF パラメータは,ソケットで使用されている送信ソケット・バッファのサイズを示します。

すべてのソケット・オプションがすべてのソケットに適用されるわけではありません。 設定できるオプションは,ソケットで使用しているアドレス・ファミリやプロトコルによって異なります。

4.3.6    データの転送

ソケット層で行われる作業の大半は,データの送受信です。 ソケット層自体は,ソケットを通して送受信されるデータに対して,いかなる構造体の使用も義務付けていません。 すべてのデータの解釈または構築は,通信ドメインのインプリメンテーションとは論理的に分離されています。

アプリケーションがデータの送受信に使用するシステム・コールは,次のとおりです。

4.3.6.1    read システム・コールの使用

read システム・コールによって,送信側のアドレスを受信せずにプロセスがソケットでデータを受信できます。

関数の構文,パラメータ,エラーについては, read(2) を参照してください。

4.3.6.2    write システム・コールの使用

write システム・コールは,接続状態のソケットで使用します。 write システム・コールを使用して転送されるデータのデスティネーションは,接続によって暗黙的に指定されます。

関数の構文,パラメータ,エラーについては, write(2) を参照してください。

4.3.6.3    send, sendto, recv および recvfrom システム・コールの使用

sendsendtorecv,および recvfrom の各システム・コールは,read および write のシステム・コールとよく似ています。 いずれも,最初の 3 つのパラメータは同じですが,sendsendtorecv,および recvfrom の各システム・コールには,追加のフラグが必要となります。 これらのフラグは <sys/socket.h> ヘッダ・ファイルに定義されており,アプリケーション・プログラムで次のうちの 1 つ以上のことを行う必要がある場合に,非ゼロの値として定義できます。

フラグ 説明
MSG_OOB 帯域外データを送受信する。
MSG_PEEK 読み取りを行わずにデータの中身を見る。 recv および recvfrom の各システム・コールで有効。
MSG_DONTROUTE パケットのルーティングを行わずにデータを送信する。 send および sendto の各システム・コールで有効。

MSG_OOB フラグは,帯域外データまたは緊急データを示し,ストリーム・ソケット (SOCK_STREAM) 固有のフラグです。 帯域外データについての詳細は,4.7.5 項を参照してください。

MSG_PEEK フラグを使用すると,アプリケーションは,読み取りに利用できるデータをプレビューでき,recv または recvfrom システム・コールが戻った後も,システムによるデータ破棄は行われません。 MSG_PEEK フラグは,recv システム・コールに指定されると,現在あるすべてのデータがユーザに返されますが,読み取られていないデータとみなされます。 したがって,そのソケットに対する次の read または recv システム・コールは,以前にプレビューされたデータを返します。

MSG_DONTROUTE フラグを使用するのは,現在のところ,ルーティング・テーブル管理プロセスだけなので,詳しくは説明しません。

send

send システム・コールは,接続状態のソケットで使用されます。 send システム・コールと write システム・コールの機能は,ほとんど同じです。 唯一の相違点は,send では,この項の最初で説明したフラグがサポートされるという点です。

関数の構文,パラメータ,エラーについては, send(2) を参照してください。

sendto

sendto システム・コールは,接続されたソケット,または接続されていないソケットで使用します。 これによって,プロセスは,メッセージのデスティネーションを明示的に指定できます。

関数の構文,パラメータ,エラーについては, sendto(2) を参照してください。

recv

recv システム・コールでは,プロセスは,送信側のアドレスを受信せずにソケットでデータを受信できます。 read システム・コールと recv システム・コールの機能は,ほとんど同じです。 唯一の相違点は,recv では,この項の最初で説明したフラグがサポートされるという点です。

関数の構文,パラメータ,エラーについては, recv(2) を参照してください。

recvfrom

recvfrom システム・コールは,接続されたソケット,または接続されていないソケットで使用できます。 recvfrom システム・コールは,recv システム・コールと機能が似ています。 ただし,recvfrom システム・コールでは,アプリケーションは,通信している対等プロセスのアドレスを受信できます。

関数の構文,パラメータ,エラーについては, recvfrom(2) を参照してください。

4.3.6.4    sendmsg および recvmsg システム・コールの使用

sendmsg および recvmsg システム・コールは,ローカル・マシン上の関連のないプロセス間で,ファイル記述子を相互に引き渡すことができるようにします。 この点で,他の送信または受信に関連するシステム・コールとは異なります。 この 2 つのシステム・コールは,アクセス権の概念をサポートする唯一のシステム・コールです。 つまり,プロセスは,システムが保持するオブジェクトにアクセスする権利を,システムによって与えられます。 sendmsg および recvmsg のシステム・コールを使用すると,このアクセス権を別のプロセスに引き渡すことができます。

sendmsg および recvmsg のシステム・コールは,アクセス権の引き渡しに msghdr データ構造体を使用します。 msghdr データ構造体は,msg_control および msg_controllen の 2 つのパラメータを定義します。 この 2 つのパラメータが,プロセス間でのアクセス権の引き渡しおよび受け取りを処理します。 msghdr データ構造体についての詳細は,4.2.3.4 項および4.5.2 項を参照してください。

sendmsg および recvmsg のシステム・コールは,コネクション指向型またはコネクションレス型のプロトコルを使用し,インターネット・ドメインまたは UNIX ドメインのいずれかで利用できますが, プロセスが記述子を引き渡すためには,そのプロセスは,UNIX ドメイン・ソケットと接続していなければなりません。

sendmsg

sendmsg システム・コールは,接続されたソケット,または接続されていないソケットで使用されます。 このシステム・コールは,msghdr データ構造体を使用して,データを転送します。 msghdr データ構造体についての詳細は,4.2.3.4 項および4.5.2 項を参照してください。

関数の構文,パラメータ,エラーについては, sendmsg(2) を参照してください。

次は,sendmsg システム・コールの例です。

struct msghdr send;
struct iovec saiov;
struct sockaddr destAddress;
char sendbuf[BUFSIZE];
  .
  .
  .
send.msg_name = (void *)&destAddress;
send.msg_namelen =  sizeof(destAddress);
send.msg_iov = &saiov;
send.msg_iovlen = 1;
saiov.iov_base = sendbuf;
saiov.iov_len = sizeof(sendbuf);
send.msg_control = NULL;
send.msg_controllen = 0;
send.msg_flags = 0;
if ((i = sendmsg(s, &send, 0)) < 0) {
        fprintf(file1,"sendmsg() failed\n");
        exit(1);
}
 

recvmsg

recvmsg システム・コールは,接続されたソケット,または接続されていないソケットで使用されます。 このシステム・コールは,msghdr データ構造体を使用してデータを転送します。 msghdr データ構造体についての詳細は,4.2.3.4 項および4.5.2 項を参照してください。

関数の構文,パラメータ,エラーについては, recvmsg(2) を参照してください。

次は,recvmsg システム・コールの例です。

struct msghdr recv;
struct iovec recviov;
struct sockaddr_in recvaddress;
char recvbuf[BUFSIZE];
  .
  .
  .
recv.msg_name = (void *) &recvaddress;
recv.msg_namelen = sizeof(recvaddress);
recv.msg_iov = &recviov;
recv.msg_iovlen = 1;
recviov.iov_base = recvbuf;
recviov.iov_len = sizeof(recvbuf);
recv.msg_control = NULL;
recv.msg_controllen = 0
recv.msg_flags = 0
if ((i = recvmsg(r, &recv, 0)) < 0) {
              fprintf(file1,"recvmsg() failed\n");
              exit(1);
}
  .
  .
  .
 

4.3.7    ソケットのシャットダウン

アプリケーション・プログラムは,すべての保留中のデータが必要でない場合には,クローズする前に,ソケットに対して shutdown システム・コールを使用できます。 関数の構文,パラメータ,エラーについては, shutdown(2) を参照してください。

4.3.8    ソケットのクローズ

close システム・コールは,ソケットのクローズに使用されます。 関数の構文,パラメータ,エラーについては, close(2) を参照してください。

ソケットのクローズ,およびそのリソースの再生は,複雑になる場合があります。 たとえば,close システム・コールは,プロセスが終了するときには,決して失敗しないと思われています。 しかし,信頼性の高いデータの引き渡しを保証しているソケットをクローズするときに,伝送するためにキューにいれられているデータがまだある場合,または受信の肯定応答を待っているデータがある場合には,ソケットはデータの伝送を行わなければなりません。 ソケットがキューに入っているデータを破棄して,close システム・コールが正常終了できるようにすると,信頼性の高いデータの引き渡しの保証に違反することになります。 データを破棄すると,close システム・コールの暗黙的な意味に依存するプロセス動作の信頼性が,ネットワーク環境において低くなります。

ただし,すべてのデータを正常に伝送するまでソケットがブロックすると,close システム・コールが終了できなくなる通信ドメインもあります。

ソケット層は,問題の解決を図りながら,close システム・コールの意味を維持しようとします。 通常のオペレーションでは,ソケットをクローズすると,キューにいれられたが受け入れられていない接続はすべて破棄されます。 ソケットが接続状態にあるときは,切断が開始されます。 ソケットは,記述子がもう参照していないことを示すようにマークされ,close オペレーションは正常に戻ります。 切断要求が終了すると,ネットワーク・サポートはソケット層へ通知し,ソケット・リソースは再生されます。 ネットワーク層は,ソケットの送信バッファに入っているすべてのデータの伝送を試みます。 しかし,伝送が正常終了する保証はされません。

一方,ソケットを明示的にマークして,クローズ時に,保留中のデータがフラッシュされて接続がシャットダウンされるまで,アプリケーション・プログラムで強制的にリンガする (残す) ことができます。 このオプションは,setsockopt システム・コールを SO_LINGER オプションとともに使用することによって,ソケット・データ構造体内にマークされます。

注意

リンガ・オプションを使用する setsockopt システム・コールは,<sys/socket.h> ヘッダ・ファイルに定義されている linger 構造体を使用します。

アプリケーション・プログラムでソケットをリンガすることを指定する場合には,リンガする期間も指定します。 切断が完了する前にリンガ期間が満了すると,ソケット層は,すべての保留中のデータを破棄して,強制的にソケットをシャットダウンします。

4.4    インターネット・アプリケーションの作成

この節では,4.3 節の情報を基にしてインターネット・ドメイン (IPv4 および IPv6) のアプリケーションを作成して使用するために必要な手順の概要について説明します。 またこの節では,アプリケーションが使用する可能性のあるアドレス・テスト・マクロについて概要を示します。 IPv6 アプリケーションを開発するには,ここで説明する情報と,4.7.1 項の移植ガイドラインを使用してください。

4.4.1    IPv4 アプリケーションの作成

現在のインターネット・アプリケーションは,IPv4 通信に AF_INET ソケットを使用します。 図 4-2 では,AF_INET ソケットを使用して IPv4 パケットを送信するクライアント・アプリケーションのイベントのシーケンス例を示しています。

図 4-2:  IPv4 通信での AF_INET ソケットの使用

  1. アプリケーションは gethostbyname を呼び出し,ホスト名 host1 を渡す。

  2. 検索によって hosts データベースから host1 を発見し,gethostbyname は IPv4 アドレス 1.2.3.4 を,hostent 型の構造体に返す。

  3. アプリケーションは socket を呼び出し,AF_INET ソケットを作成する。 この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。

  4. socket 呼び出しが成功すると,アプリケーションは sockaddr_in 構造体を設定して connect を呼び出し,host1 とのコネクションを確立する。 connect 呼び出しが成功すると,アプリケーションは send を呼び出して,情報をアドレス 1.2.3.4 に送信する。

  5. ソケット層は,情報とアドレスを UDP モジュールに渡す。

  6. UDP モジュールはアドレス 1.2.3.4 をパケット・ヘッダにいれ,情報を IPv4 に渡し伝送する。

これ以降,アプリケーションは次の処理を実行できます。

  1. recv を呼び出し,サーバ・システムからの応答を待つ。

  2. 応答を受信した後,gethostbyaddr を呼び出し,sockaddr_in 構造体でサーバのアドレスを渡す。 検索によりそのアドレスがホスト・データベース内で見つかると,gethostbyaddr は,hostent 型の構造体でホスト名を返す。

  3. inet_ntoa を呼び出して,サーバ・アドレスをテキスト文字列に変換する。

C.1.1 項には,これらの手順を示す,クライアント・プログラム・コード例があります。

4.4.2    IPv6 アプリケーションの作成

インターネット・アプリケーションは,IPv6 通信に AF_INET6 ソケットを使用できます。 さらに AF_INT6 ソケットでは,IPv4 通信もできます。 図 4-3 では,AF_INET6 ソケットを使用して IPv6 パケットを送信するクライアント・アプリケーションのイベントのシーケンスを示しています。

図 4-3:  IPv6 通信での AF_INET6 ソケットの使用

  1. アプリケーションは getaddrinfo を呼び出し,ホスト名 (host1),AF_INET6 アドレス・ファミリ・ヒント,AI_ADDRCONFIG フラグ・ヒント,AI_V4MAPPED フラグ・ヒントを渡す。 このフラグ・ヒントは,host1 の IPv6 アドレスが見つかった場合は,それを返すように関数に指示する。 ヒントのフィールドと値についての詳細は, getaddrinfo(3) を参照してください。

  2. 検索によって hosts データベースで host1 の IPv6 アドレスを発見し,getaddrinfo は IPv6 アドレス 3ffe:1200::a00:2bff:fe2d:02b2 を,1 つ以上の addrinfo 型の構造体に返す。

  3. アプリケーションは socket を呼び出し,addrinfo 構造体内のアドレス・ファミリおよびソケット・タイプを使用して,AF_INET6 ソケットを作成する。 この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。

  4. socket 呼び出しが成功すると,アプリケーションは addrinfo 構造体内のホスト・アドレスと長さを使用して connect を呼び出し,host1 とのコネクションを確立する。 connect 呼び出しが成功すると,アプリケーションは情報をアドレス 3ffe:1200::a00:2bff:fe2d:02b2 に送信する。

    注意

    addrinfo 構造体の情報を使用した後,アプリケーションは freeaddrinfo を呼び出して,この構造体に使用されていたシステム・リソースを解放します。

  5. ソケット層は,情報とアドレスを UDP モジュールに渡す。

  6. UDP モジュールは IPv6 アドレスを識別し,アドレス 3ffe:1200::a00:2bff:fe2d:02b2 をパケット・ヘッダにいれ,情報を IPv6 モジュールに渡し伝送する。

これ以降,アプリケーションは次の処理を実行できます。

  1. recv を呼び出し,サーバ・システムからの応答を待つ。

  2. 応答を受信した後,getpeername を呼び出し,接続されたソケットのアドレスを調べる。 このアドレスは,sockaddr_in6 型の構造体で返される。 アプリケーションがプロトコルに依存しないようにしたい場合は,sockaddr_in6 構造体の代わりに sockaddr_storage 構造体を使用することもできる。

  3. NI_NAMEREQD フラグを指定して getnameinfo を呼び出し,サーバ名を取得する。

  4. NI_NUMERICHOST フラグを指定して getnameinfo を呼び出し,サーバ・アドレスをテキスト文字列に変換する。

C.2.1 項 には,これらの手順を示す,クライアント・プログラム・コードの例があります。

AF_INET6 ソケットは,IPv4 通信に使用することもできます。 図 4-4 では,AF_INET6 ソケットを使用して IPv4 パケットを送信するクライアント・アプリケーションのイベントのシーケンスを示しています。

図 4-4:  IPv4 通信での AF_INET6 ソケットの使用 (送信)

  1. アプリケーションは getaddrinfo を呼び出し,ホスト名 (host1),AF_INET6 アドレス・ファミリ・ヒント,AI_ADDRCONFIG フラグ・ヒント,AI_V4MAPPED フラグ・ヒントを渡す。 このフラグ・ヒントは,host1 の IPv4 アドレスが見つかった場合は,それを IPv4 にマップされた IPv6 アドレスとして返すように関数に指示する。 ヒントのフィールドと値についての詳細は, getaddrinfo(3) を参照してください。

  2. 検索によって hosts データベースで host1 の IPv4 アドレス 1.2.3.4 を発見し,getaddrinfo は IPv4 にマップされた IPv6 アドレス ::ffff:1.2.3.4 を,1 つ以上の addrinfo 型の構造体に返す。

  3. アプリケーションは socket を呼び出し,addrinfo 構造体内のアドレス・ファミリおよびソケット・タイプを使用して,AF_INET6 ソケットを作成する。 この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。

  4. socket 呼び出しが成功すると,アプリケーションは addrinfo 構造体内のホスト・アドレスと長さを使用して connect を呼び出し,host1 とのコネクションを確立する。 connect 呼び出しが成功すると,アプリケーションは情報をアドレス ::ffff:1.2.3.4 に送信する。

    注意

    addrinfo 構造体の情報を使用した後,アプリケーションは freeaddrinfo を呼び出して,この構造体に使用されていたシステム・リソースを解放します。

  5. ソケット層は,情報とアドレスを UDP モジュールに渡す。

  6. UDP モジュールは IPv4 にマップされた IPv6 アドレスを識別し,アドレス 1.2.3.4 をパケット・ヘッダにいれ,情報を IPv4 モジュールに渡し伝送する。

これ以降,アプリケーションは次の処理を実行できます。

  1. recv を呼び出し,サーバ・システムからの応答を待つ。

  2. 応答を受信した後,getpeername を呼び出し,接続されたソケットのアドレスを調べる。 このアドレスは,sockaddr_in6 型の構造体で返される。 アプリケーションがプロトコルに依存しないようにしたい場合は,sockaddr_in6 構造体の代わりに sockaddr_storage 構造体を使用することもできる。

  3. NI_NAMEREQD フラグを指定して getnameinfo を呼び出し,サーバ名を取得する。

  4. NI_NUMERICHOST フラグを指定して getnameinfo を呼び出し,サーバ・アドレスをテキスト文字列に変換する。

C.2.1 項 には,これらの手順を示す,クライアント・プログラム・コードの例があります。

AF_INET6 ソケットは,IPv4 アドレスまたは IPv6 アドレスのどちらに送られたメッセージも受信できます。 AF_INET6 ソケットは,IPv4 アドレスを表すために IPv4 にマップされた IPv6 アドレス・フォーマットを使用します。 図 4-5 では,AF_INET6 ソケットを使用して IPv4 パケットを受信するサーバ・アプリケーションのイベントのシーケンスを示しています。

図 4-5:  IPv4 通信での AF_INET6 ソケットの使用 (受信)

  1. アプリケーションは次の処理を行う。

    1. socket を呼び出して,AF_INET6 ソケットを作成する。

    2. sockaddr_in6 構造体を初期化し,ファミリ,アドレス,ポートを設定する。

    3. bind を呼び出して,ソケットにアドレスを割り当てる。

    4. listen を呼び出して,ソケットをコネクション受け付け状態にする。

  2. IPv4 パケットが到着し,IPv4 モジュールを通過する。

  3. TCP レイヤでは,パケット・ヘッダを削除し,その情報と IPv4 にマップされた IPv6 アドレス (::ffff:1.2.3.4) をソケット・レイヤへ渡す。

  4. アプリケーションは accept を呼び出し,ソケットから情報を取り出す。 ソケットからの情報は,sockaddr_storage 構造体でアプリケーションに渡される。 これにより,アプリケーションがプロトコルに依存しなくなる。

  5. アプリケーションは getnameinfo を呼び出し,アドレス ::ffff:1.2.3.4NI_NAMEREQD フラグを渡す。 このフラグは,アドレスに対応するホスト名を返すように関数に指示する。 フラグ・ビットとその意味についての詳細は, getnameinfo(3) を参照してください。

  6. 検索の結果,アドレス 1.2.3.4 のホスト名が hosts データベースに見つかり,getnameinfo はホスト名を返す。

C.2.2 項 には,これらの手順を示す,サーバ・プログラム・コードの例があります。

4.4.3    アドレス・テスト・マクロ

AF_INET6 ソケットを通信に使用するアプリケーションは,場合によっては,構造体に返されるアドレスのタイプを調べる必要があります。 このような場合のために,API にはアドレスをテストするマクロが定義されています。 表 4-5 に,現在定義されているアドレス・テスト・マクロと,有効なテストで返される値をリストします。 これらのマクロを使用するには,アプリケーション内で次のファイルをインクルードします。

#include <netinet/in.h>

表 4-5:  アドレス・テスト・マクロの要約

マクロ 返却値
IN6_IS_ADDR_UNSPECIFIED 指定されたタイプの場合,True。
IN6_IS_ADDR_LOOPBACK 指定されたタイプの場合,True。
IN6_IS_ADDR_MULTICAST 指定されたタイプの場合,True。
IN6_IS_ADDR_LINKLOCAL 指定されたタイプの場合,True。
IN6_IS_ADDR_SITELOCAL 指定されたタイプの場合,True。
IN6_IS_ADDR_V4MAPPED 指定されたタイプの場合,True。
IN6_IS_ADDR_V4COMPAT 指定されたタイプの場合,True。
IN6_IS_ADDR_MC_NODELOCAL 指定されたスコープの場合,True。
IN6_IS_ADDR_MC_LINKLOCAL 指定されたスコープの場合,True。
IN6_IS_ADDR_MC_SITELOCAL 指定されたスコープの場合,True。
IN6_IS_ADDR_MC_ORGLOCAL 指定されたスコープの場合,True。
IN6_IS_ADDR_MC_GLOBAL 指定されたスコープの場合,True。
IN6_ARE_ADDR_EQUAL アドレスが等しい場合,True。

4.5    BSD ソケット・インタフェース

XNS4.0 ソケット・インタフェースに加えて,オペレーティング・システムは,4.3BSD,4.4BSD および POSIX 1003.1g Draft 6.6 のソケット・インタフェースもサポートしています。 4.4BSD ソケット・インタフェースは,4.3BSD ソケットに多数の変更を加えています。 4.3BSD と 4.4BSD のソケット・インタフェースの間の変更のほとんどは,ソケット・フレームワークのもとで,国際標準化機構 (ISO) プロトコル群のインプリメンテーションを容易にすることを目的としています。 XNS4.0 ソケット・インタフェースは,標準のソケット・インタフェースを提供しています

注意

4.4BSD が利用できても,そのサイトが ISO プロトコルをサポートするということではありません。 それぞれのサイトの適切な担当者に問い合わせてください。

4.4BSD ソケット・インタフェースを使用するには,プログラムまたはメイクファイルに次の行を追加しなければなりません。

#define _SOCKADDR_LEN
 

4.4BSD ソケット・インタフェースには,アプリケーション・プログラム用に,4.3BSD インタフェースから次の変更が加えられています。

以降の項で,これらの機能について説明します。

4.5.1    可変長ネットワーク・アドレス

4.4BSD の sockaddr 構造体は,可変長のネットワーク・アドレスをサポートしています。 構造体に長さのフィールドが加わり,次のように定義されています。

/* 4.4BSD sockaddr Structure */
 
struct sockaddr {
        u_char sa_len;      /* total length */
        u_char sa_family;   /* address family */
        char   sa_data[14]; /* actually longer; address value */
};
 

4.3BSD の sockaddr 構造体には,次のフィールドがあります。

u_short  sa_family;
char     sa_data[14];
 

図 4-6では,4.4BSD と 4.3BSD の sockaddr 構造体を比較しています。

図 4-6:  4.3BSD と 4.4BSD の sockaddr 構造体

4.5.2    プロトコル・データとユーザ・データの受信

4.3BSD バージョンの msghdr 構造体 (cc コマンドを使用するときの省略時の設定) は,sendmsg および recvmsg の各システム・コールのオプション機能を使用するときに必要なパラメータを提供します。

4.3BSD の msghdr 構造体は,次のとおりです。

/* 4.3BSD msghdr Structure */
struct msghdr {
        caddr_t msg_name;               /* optional address */
        int     msg_namelen;            /* size of address */
        struct  iovec *msg_iov;         /* scatter/gather array */
        int     msg_iovlen;             /* # elements in msg_iov */
        caddr_t msg_accrights;          /* access rights sent/re-
                                        /* ceived */
        int     msg_accrightslen;
};
 

パラメータ msg_name および msg_namelen は,ソケットが接続されていないときに使用します。 パラメータ msg_iov および msg_iovlen は,分散 (読み取り) および収集 (書き込み) オペレーションで使用します。 前述したように,パラメータ msg_accrights および msg_accrightslen によって,送信プロセスは,受信プロセスにアクセス権を引き渡すことができます。

4.4BSD 構造体には追加フィールドがあり,これによって,アプリケーション・プログラムは,メッセージ内にユーザ・データとともにプロトコル情報を含めることができます。

ユーザ・データとともにプロトコル・データを受信できるようにするために,オペレーティング・システムは,4.4BSD ソケット・インタフェースからの msghdr 構造体を提供しています。 構造体には,制御データへのポインタ,制御データの長さを表す長さフィールド,およびフラグ・フィールドが,次のように追加されています。

 /* 4.4BSD msghdr Structure */
struct msghdr {
        caddr_t msg_name;               /* optional address */
        u_int   msg_namelen;            /* size of address */
        struct  iovec *msg_iov;         /* scatter/gather array */
        u_int   msg_iovlen;             /* # elements in msg_iov */
        caddr_t msg_control;            /* ancillary data, see below */
        u_int   msg_controllen;         /* ancillary data buffer len */
        int     msg_flags;              /* flags on received message */
};
 

XNS4.0 および POSIX 1003.1g Draft 6.6 の msghdr データ構造体は,4.4BSD と同じフィールドを持っています。 ただし,msg_namelen および msg_controllen フィールドのサイズは,4.4BSD の msghdr データ構造体では 4 バイトなのに対して,XNS4.0 および POSIX 1003.1g Draft 6.6 の msghdr データ構造体では 8 バイトです。 さらに,msg_iovlen フィールドのサイズが,4.4BSD および XNS4.0 の msghdr データ構造体では 4 バイト長であるのに対し,POSIX 1003.1g Draft 6.6 の msghdr データ構造体では 8 バイトです。 図 4-7 に,4.3BSD,4.4BSD,XPG4,および POSIX 1003.1g Draft 6.6 の msghdr 構造体を示します。

図 4-7:  4.3BSD,4.4BSD,XNS4.0,および POSIX 1003.1g の msghdr 構造体

4.3BSD の msghdr 構造体では,送信側プロセスは,msg_accrights および msg_accrightslen フィールドで,システムが維持するオブジェクトへのアクセス権を受信側プロセスに渡すことができます。 この場合,システムが維持するオブジェクトとはソケットです。 4.4BSD,XNS4.0,および POSIX 1003.1g Draft 6.6 では,msg_control および msg_controllen フィールドで渡すことができます。

4.6    一般的なソケット・エラー

表 4-6 は,一般的なソケット・エラー・メッセージと,そのメッセージが示す問題の一覧です。

表 4-6:  一般的なエラーと診断

エラー 診断
[EAFNOSUPPORT] 指定したアドレス・ファミリのアドレスを,プロトコル・ファミリがサポートしていない。
[EBADF] ソケット・パラメータが有効でない。
[ECONNREFUSED] 接続の試みがリジェクトされた。
[EFAULT] ポインタが,ユーザ・アドレス空間の有効な部分を指し示していない。
[EHOSTDOWN] ホストがダウンしている。
[EHOSTUNREACH] ホストに到達できない。
[EINVAL] 誤った引数を使用している。
[EMFILE] 現在のプロセスにあるオープンしたファイル記述子が多すぎる。
[ENETDOWN] ネットワークがダウンしている。
[ENETUNREACH] ネットワークに到達できない。 ネットワークへの経路がない。
[ENOMEM] システムが,プロセス記述子テーブルを増やすためのカーネル・メモリを割り当てることができなかった。
[ENOTSOCK] ソケット・パラメータが,ソケットではなくファイルを参照している。
[EOPNOTSUPP] 指定したプロトコルでは,ソケットのペアを作成できない。
[EOPNOTSUPP] 参照したソケットは,接続を受け入れることができない。
[EPROTONOSUPPORT] 指定したプロトコルを,このシステムがサポートしていない。
[EPROTOTYPE] 指定したプロトコルで,ソケット・タイプをサポートしていない。
[ETIMEDOUT] リモート・アプリケーションからの応答がないまま,接続がタイム・アウトになった。
[EWOULDBLOCK] ソケットが非ブロッキングとマークされており,作業を完了できなかった。

4.7    高度なソケット・プログラミング情報

この節では,次の項目について説明します。これは,ソケット用に複雑なアプリケーションを作成するプログラマにとって有効な情報です。

4.7.1    AF_INET6 ソケットを使用するための,アプリケーションの移植

AF_INET6 ソケットにより,アプリケーションは IPv6 プロトコル,IPv4 プロトコル,またはその両方を使用して通信できるようになります。 IPv6 通信については,RFC 2553 の『Basic Socket Interface Extensions for IPv6』で,BSD ソケットのアプリケーション・プログラミング・インタフェース (API) への変更点が規定されています。 この変更点の要約を,表 4-7 に示します。

表 4-7:  BSD ソケット API に対する IPv6 拡張の要約

カテゴリ 変更点
コア関数の呼び出し なし。 ソケット関数の基本的な構文は同じです。 ソケット関数を使用するとき,アプリケーションではプロトコル固有のアドレス構造体へのポインタを,汎用の sockaddr アドレス構造体へのポインタにキャストしなければなりません。 インターネット・アプリケーションの作成については,4.4 節を参照してください。
ソケット・アドレス構造体 IPv6 通信用に新しい sockaddr_in6 構造体,プロトコル非依存の通信用に sockaddr_storage 構造体が規定されました。 sockaddr_in 構造体は,IPv4 通信用に残されています。 詳細は,4.7.1.2 項を参照してください。
名前からアドレスへの変換 プロトコル非依存 (IPv4 および IPv6) の通信用に,getnameinfo 関数と getaddrinfo 関数が規定されました。 gethostbyaddr 関数と gethostbyname 関数は,IPv4 通信用に残されています。 詳細は,4.7.1.3 項を参照してください。
アドレス変換関数 プロトコル非依存 (IPv4 および IPv6) のアドレス変換用に,getnameinfo 関数と getaddrinfo 関数が規定されました。 inet_ntoa 関数と inet_addr 関数は,IPv4 アドレス変換用に残されています。 詳細は,4.7.1.3 項を参照してください。
ソケット・オプション IPv6 マルチキャスト用の新しいソケット・オプションが規定されました。 詳細は,4.7.6 項を参照してください。

この項では,IPv6 ネットワーク環境で動作させるために既存のアプリケーション・コードに対して行わなければならない,次の変更について説明します。 また,インターネット・アプリケーションが AF_INET6 ソケット上でどのように動作するかも知っていなければなりません。 詳細は,4.4.2 項を参照してください。

この項では,既存の IPv4 アプリケーションと連携して動作させる必要のあるアプリケーションについても,コードを変更する際のガイドラインを説明します。 これらの変更を行うと,移植後のアプリケーションは,IPv4 と IPv6 の両方で通信できるようになります。 既存の IPv4 アプリケーションは以前と同じように動作し,IPv6 アプリケーションと連携して動作させることができます。

移植作業を容易にするために,オペレーティング・システムには ipv6_sniff ユーティリティが用意されています。 このユーティリティを使うと,ソース・ファイルを走査して変更が必要なトークンを見つけ,必要であればソース・ファイルを編集して変更することができます。 このユーティリティの使い方については,4.7.1.5 項を参照してください。

4.7.1.1    名前の変更

変更のほとんどは単純で機械的なものですが,コードの再構成が少し必要になることもあります (たとえば,IPv4 アドレスを保持する int データ型を返すルーチンでは,in6_addr へのポインタのパラメータを追加して,そこに IPv6 アドレスを書き込まなければならないことがあります)。 表 4-8 は,アプリケーションのコードに対して行う変更の要約です。

表 4-8:  名前の変更

変更前 変更後
AF_INET AF_INET6
PF_INET PF_INET6
INADDR_ANY in6addr_any

4.7.1.2    構造体の変更

次の構造体は,構造体名とフィールド名が変更されています。

以降の項で,これらの変更について説明します。

in_addr 構造体

in_addr 構造体を使用するアプリケーションは,in6_addr 構造体を使用するように必要に応じて変更しなければなりません。

AF_INET の構造体 AF_INET6 の構造体

struct in_addr [1]
  unsigned int s_addr [2]

struct in6_addr [1]
  uint8_t s6_addr [16] [2]

必要に応じて,アプリケーションを次のように変更します。

  1. 構造体名 in_addrin6_addr に変更する。 [例に戻る]

  2. データ型を unsigned int から uint8_t に,フィールド名を s_addr から s6_addr に変更する。 [例に戻る]

in6_addr 構造体を使用する場合は,この他にもアプリケーション対して変更が必要となる場合がありますので,4.7.1.4 項を参照してください。

sockaddr_in 構造体

4.4 BSD の sockaddr_in 構造体を使用するアプリケーションは,次の例に示すように,sockaddr_in6 構造体を使用するように必要に応じて変更しなければなりません。

AF_INET の構造体 AF_INET6 の構造体

struct sockaddr_in [1]
  unsigned char sin_len [2]
  sa_family_t sin_family [3]
  in_port_t sin_port [4]
  struct in_addr sin_addr [5]

struct sockaddr_in6 [1]
  uint8_t sin6_len [2]
  sa_family_t sin6_family [3]
  int_port_t sin6_port [4]
  struct in6_addr sin6_addr [5]

必要に応じて,アプリケーションを次のように変更します。

  1. 構造体名 sockaddr_insockaddr_in6 に変更する。 [例に戻る]

  2. データ型を unsigned char から uint8_t に,フィールド名を sin_len から sin6_len に変更する。 [例に戻る]

  3. フィールド名を sin_family から sin6_family に変更する。 [例に戻る]

  4. フィールド名を sin_port から sin6_port に変更する。 [例に戻る]

  5. フィールド名を sin_addr から sin6_addr に変更する。 [例に戻る]

4.3 BSD の sockaddr_in 構造体を使用するアプリケーションは,次の例に示すように,sockaddr_in6 構造体を使用するように必要に応じて変更しなければなりません。

AF_INET の構造体 AF_INET6 の構造体

struct sockaddr_in [1]
  u_short sin_family [2]
  in_port_t sin_port [3]
  struct in_addr sin_addr [4]

struct sockaddr_in6 [1]
  u_short sin6_family [2]
  in_port_t sin6_port [3]
  struct in6_addr sin6_addr [4]

必要に応じて,アプリケーションを次のように変更します。

  1. 構造体名 sockaddr_insockaddr_in6 に変更する。 [例に戻る]

  2. フィールド名 sin_familysin6_family に変更する。 [例に戻る]

  3. フィールド名 sin_portsin6_port に変更する。 [例に戻る]

  4. フィールド名 sin_addrsin6_addr に変更する。 [例に戻る]

注意

どちらの場合も,構造体を宣言した後に,sockaddr_in6 構造体全体をゼロで初期化しなければなりません。

sockaddr 構造体

汎用のソケット・アドレス構造体 (sockaddr) を用いて AF_INET ソケット・アドレス (sockaddr_in) を保持しているアプリケーションは,AF_INET6 の sockaddr_in6 構造体か sockaddr_storage 構造体を使用するように変更しなければなりません。

アプリケーションが IPv6 アドレスだけを扱う場合は,必要に応じて次の変更をアプリケーションに行います。

AF_INET の構造体 AF_INET6 の構造体

struct sockaddr [1]

struct sockaddr_in6 [1]

  1. struct sockaddr_in にすべきところは,構造体名 sockaddrsockaddr_in6 に変更する。 たとえば,sizeof(struct sockaddr)[例に戻る]

アプリケーションが IPv4 と IPv6 の両方のノードから送られてくるアドレスを操作する場合は,必要に応じて次の変更をアプリケーションに行います。

AF_INET の構造体 AF_INET6 の構造体

struct sockaddr [1]

struct sockaddr_storage [1]

  1. struct sockaddr_in にすべきところは,構造体名 sockaddrsockaddr_storage に変更する。 たとえば,sizeof(struct sockaddr)[例に戻る]

注意

sockaddr_in6 構造体と sockaddr_storage 構造体は,どちらもサイズが sockaddr 構造体よりも大きくなっています。

hostent 構造体

hostent 構造体を使用するアプリケーションは,次の例に示すように,addrinfo 構造体を使用するように必要に応じて変更しなければなりません。

AF_INET の構造体 AF_INET6 の構造体

struct hostent [1]

struct addrinfo [1]

必要に応じて,アプリケーションを次のように変更します。

  1. 構造体名 hostentaddrinfo に変更する。 [例に戻る]

4.7.1.3    関数呼び出しの変更

次のライブラリ・ルーチンを使用しているアプリケーションは,必要に応じて変更しなければなりません。

以降の項で,これらの変更について説明します。

gethostbyaddr 関数呼び出し

gethostbyaddr 関数を呼び出しているアプリケーションは,次の例に示すように,getnameinfo 関数を呼び出すように変更しなければなりません。

AF_INET の呼び出し

gethostbyaddr(xxx,4,AF_INET) [1]

AF_INET6 の呼び出し

err=getnameinfo(&sockaddr,sockaddr_len, node_name, name_len,
 service, service_len, flags); [1]

必要に応じて,アプリケーションを次のように変更します。

  1. 関数名を gethostbyaddr から getnameinfo に変更し,ソケット・アドレス構造体へのポインタ,返されるノード名用の文字列,返されるノード名の長さを示す整数値,返されるサービス名用の文字列,返されるサービス名の長さを示す整数値,実行されるアドレス処理のタイプを示す整数値を指定する。 フラグ・ビットとその意味についての詳細は, getnameinfo(3) を参照してください。 [例に戻る]

gethostbyname 関数呼び出し

gethostbyname 関数を呼び出すアプリケーションは,次の例に示すように,getaddrinfo 関数を呼び出すように変更しなければなりません。

AF_INET の呼び出し

gethostbyname(name) [1]

AF_INET6 の呼び出し

err=getaddrinfo(node_name, service_name, &hints, &result); [1]

.
.
.
freeaddrinfo(&result); [2]

必要に応じて,アプリケーションを次のように変更します。

  1. 関数名を gethostbyname から getaddrinfo に変更し,ノード名の文字列,使用するサービス名の文字列,処理オプションが格納されている hints 構造体へのポインタ,addrinfo 構造体 (アドレス情報が返される構造体) へのポインタを指定する。 ヒントのフィールドと値についての詳細は, getaddrinfo(3) を参照してください。 [例に戻る]

  2. freeaddrinfo ルーチンの呼び出しを追加して,アプリケーションが使用を終えたときに,addrinfo 構造体を解放する。 [例に戻る]

アドレスが IPv4 アドレスと IPv6 アドレスのどちらかであるかをアプリケーションで調べる必要があるが,アドレス・ファミリからは判断できない場合は,IN6_IS_ADDR_V4MAPPED マクロを使用してください。 IPv6 アドレス・テスト・マクロの一覧については,4.4.3 項を参照してください。

inet_ntoa 関数呼び出し

inet_ntoa 関数を呼び出しているアプリケーションは,次の例に示すように,getnameinfo 関数を呼び出すように変更しなければなりません。

AF_INET の呼び出し

inet_ntoa(addr) [1]

AF_INET6 の呼び出し

err=getnameinfo(&sockaddr,sockaddr_len, node_name, name_len,
 service, service_len, NI_NUMERICHOST); [1]
 
 

必要に応じて,アプリケーションを次のように変更します。

  1. 関数名を gethostbyaddr から getnameinfo に変更し,ソケット・アドレス構造体へのポインタ,返されるノード名用の文字列,返されるノード名の長さを示す整数値,返されるサービス名用の文字列,返されるサービス名の長さを示す整数値,NI_NUMERICHOST フラグを指定する。 フラグ・ビットとその意味についての詳細は, getnameinfo(3) を参照してください。 [例に戻る]

inet_addr 関数呼び出し

inet_addr 関数を呼び出すアプリケーションは,次の例に示すように,getaddrinfo 関数を呼び出すように変更しなければなりません。

AF_INET の呼び出し

result=inet_addr(&string); [1]

AF_INET6 の呼び出し

err=getaddrinfo(node_name, service_name, &hints, &result); [1]

.
.
.
freeaddrinfo(&result); [2]

必要に応じて,アプリケーションを次のように変更します。

  1. 関数名を inet_addr から getaddrinfo に変更し,ノード名の文字列,使用するサービス名の文字列,AI_NUMERICHOST オプションが格納されている hints 構造体へのポインタ,addrinfo 構造体 (アドレス情報が返される構造体) へのポインタを指定する。 ヒントのフィールドと値についての詳細は, getaddrinfo(3) を参照してください。 [例に戻る]

  2. freeaddrinfo ルーチンの呼び出しを追加して,アプリケーションが使用を終えたときに,addrinfo 構造体を解放する。 [例に戻る]

アドレスが IPv4 アドレスと IPv6 アドレスのどちらであるかをアプリケーションで調べる必要があるが,アドレス・ファミリからは判断できない場合は,IN6_IS_ADDR_V4MAPPED マクロを使用してください。 IPv6 アドレス・テスト・マクロの一覧については,4.4.3 項を参照してください。

4.7.1.4    アプリケーションの他の変更

名前の変更以外にも,コードを見直して IP アドレス情報や変数を使用しているところを個別に確認してください。

IP アドレスの比較

アプリケーションが IP アドレスを比較したり,IP アドレスが等しいかテストしている場合,4.7.1.2 項 で行った in6_addr 構造体の変更により,int の数値の比較が構造体の比較に変わります。 これによってコードが誤った状態になり,コンパイル時にエラーになります。

必要に応じて,アプリケーションを次のように変更します。

AF_INET のコード

(addr1->s_addr == addr2->s_addr) [1]

AF_INET6 のコード

(memcmp(addr1, addr2, sizeof(struct in6_addr)) == 0) [1]

  1. 等値比較式を,memcmp (メモリの比較) 関数を使用したものに変更する。 [例に戻る]

AF_INET のコード AF_INET6 のコード

(addr1->s_addr == addr2->s_addr) [1]

IN6_ARE_ADDR_EQUAL(addr1, addr2) [1]

  1. 等値比較式を,IN6_ARE_ADDR_EQUAL マクロを使用したものに変更する。 IPv6 アドレス・テスト・マクロの一覧については,4.4.3 項を参照してください。 [例に戻る]

IP アドレスをワイルドカード・アドレスと比較

アプリケーションが IP アドレスをワイルドカード・アドレスと比較している場合,4.7.1.2 項 で行った in6_addr 構造体への変更により,int の数値の比較が構造体の比較に変わります。 これによってコードが誤った状態になり,コンパイル時にエラーになります。

必要に応じて,アプリケーションを次のように変更します。

AF_INET のコード AF_INET6 のコード

(addr->s_addr == INADDR_ANY) [1]

IN6_IS_ADDR_UNSPECIFIED(addr) [1]

  1. 等値比較式を,IN6_IS_ADDR_UNSPECIFIED マクロを使用したものに変更する。 IPv6 アドレス・テスト・マクロの一覧については,4.4.3 項を参照してください。 [例に戻る]

AF_INET のコード

(addr->s_addr == INADDR_ANY) [1]

AF_INET6 のコード

(memcmp(addr, in6addr_any, sizeof(struct in6_addr)) == 0) [1]

  1. 等値比較式を,memcmp (メモリの比較) 関数を使用したものに変更する。 [例に戻る]

int データ型による IP アドレスの保持

アプリケーションが int データ型を用いて IP アドレスを保持している場合,4.7.1.2 項 で行ったin6_addr 構造体への変更によって,代入が変わります。 これによってコードが誤った状態になり,コンパイル時にエラーになります。

必要に応じて,アプリケーションを次のように変更します。

AF_INET のコード AF_INET6 のコード

struct in_addr foo;
int bar; [1]

.
.
.
bar = foo.s_addr; [2]

struct in6_addr foo;
struct in6_addr bar; [1]

.
.
.
bar = foo; [2]

  1. bar のデータ型を int から struct in6_addr に変更する。 [例に戻る]

  2. bar への代入文を変更して,s_addr フィールドの参照を削除する。 [例に戻る]

IP アドレスを返す関数の使用

IP アドレスを int データ型で返す関数をアプリケーションで使用している場合,4.7.1.2 項 で行った in6_addr 構造体の変更により,戻り値の代入先が int から char の配列に変わります。 これによってコードが誤った状態になり,コンパイル時にエラーになります。

必要に応じて,アプリケーションを次のように変更します。

AF_INET のコード AF_INET6 のコード

struct in_addr *addr;
addr->s_addr = foo(xxx); [1]

struct in6_addr *addr;
foo(xxx, addr); [1]

  1. 関数を再構築して,呼び出し時に構造体のアドレスを渡せるようにする。 さらに,関数を変更して addr が指す構造体の中に戻り値を書き込む。 [例に戻る]

ソケット・オプションの変更

アプリケーションが IPv4 IP レベルのソケット・オプションを使用している場合,これを対応する IPv6 オプションに変更します。 詳細は, ip(7) を参照してください。

4.7.1.5    ipv6_sniff ユーティリティの使用

ipv6_sniff ユーティリティは,1 個以上のファイルを走査して IPv6 へ移植する上で問題となる可能性のある部分を検出します。 特に指定しなければ,このユーティリティは IPv4 専用ソケットの使用およびオプションと,IPv4 の名前およびアドレス解決を探します。 ファイルの走査が終わると,このユーティリティは結果をソートして報告します。 このユーティリティ内からユーザの好みに合ったエディタを起動し,ファイルを参照したり,変更することもできます。

このユーティリティを実行するには,次のコマンドを入力します。

# /usr/sbin/ipv6sniff filename...

詳細については, ipv6_sniff(8) を参照してください。

4.7.2    IPv6 raw ソケットの使用

raw ソケットは,IPv4 と IPv6 の両方で使われ,TCP および UDP のトランスポート層をバイパスします。 表 4-9 に,IPv4 raw ソケットと IPv6 raw ソケットの主な相違点を示します。

表 4-9:  IPv4 raw ソケットと IPv6 raw ソケットの相違点

  IPv4 IPv6
用途 ICMPv4,IGMPv4 のアクセスと,カーネルが認識しないプロトコル・フィールドを含む IPv4 データグラムの読み書き。 ICMPv6 のアクセスと,カーネルが認識しない次ヘッダ・フィールドを含む IPv6 データグラムの読み書き。
バイト順 規定していない。 すべてのデータ送受信で,ネットワーク・バイト順。
完全なパケットの送受信 はい。 いいえ。 補助データ・オブジェクトを使って,拡張ヘッダとホップ限界情報を転送します。

アプリケーションが完全な IPv6 パケットをアクセスする必要がある場合は,DLPI (Data Link Provider Interface) を使います。

出力の場合,補助データまたはソケット・オプションを使うと,すべてのフィールド (フロー・ラベル・フィールドを除く) を変更できます。 入力の場合,アプリケーションはすべてのフィールド (フロー・ラベル,バージョン番号,次ヘッダ・フィールド,およびすべての拡張ヘッダを除く) を,補助データを使ってアクセスできます。

ICMPv6 raw ソケット以外の IPv6 raw ソケットでは,アプリケーションは IPV6_CHECKSUM ソケット・オプションを設定しなければなりません。 たとえば,次のようにします。

int offset = 2;
setsockopt (fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
 

これによりカーネルは,出力時にはチェックサムを計算して格納し,入力時にはチェックサムを検査できるようになります。 このためアプリケーションは,すべての出力パケットでソース・アドレスを選択する必要がなくなります。 このソケット・オプションは,省略時の設定では無効です。 オフセット変数に -1 を設定しても,このオプションを無効にできます。

IPv6 raw ソケットを使うと,アプリケーションは次の情報にアクセスできます。

この項では,これらの情報にアクセスする方法について説明します。

4.7.2.1    ICMPv6 メッセージへのアクセス

ICMPv6 raw ソケットは,socket 関数を PF_INET6,SOCK_RAW,および IPPROTO_ICMPV6 引数で呼び出したときに作成されるソケットです。 カーネルは,すべての送信 ICMPv6 パケットに ICMPv6 チェックサムを計算して挿入し,すべての受信パケットのチェックサムを検査します。 受信チェックサムが不正の場合,パケットは廃棄されます。

ICMPv6 は ICMPv4 のスーパセットのため,ICMPv6 raw ソケットは ICMPv4 raw ソケットよりも多くのメッセージを受信できます。 特に指定しなければ,ICMPv6 raw ソケットを作成すると,ソケットはすべてのタイプの ICMPv6 メッセージをアプリケーションに渡します。 ただし,アプリケーションは,すべてのメッセージをアクセスする必要はありません。 アプリケーションは,ICMPv6 フィルタを作成することで,渡される ICMPv6 メッセージのタイプを指定できます。

ICMPv6 フィルタのデータ型は,struct icmp6_filter です。 getsockopt を使って現在のフィルタを取り出し,setsockopt を使ってフィルタを格納します。 たとえば,ICMPv6 メッセージのフィルタリングを有効にするには,次のように ICMP6_FILTER オプションを使います。

struct icmp6_filter myfilter;
setsockopt (fd, IPPROTO_ICMPV6, IPV6_FILTER, &myfilter, sizeof(myfilter));
 

myfilter の値は,ICMPv6 のメッセージ・タイプ (0 〜 255) です。

表 4-10 に,すべての ICMPv6 フィルタ・マクロとその説明を示します。

表 4-10:  ICMPv6 フィルタリング・マクロの要約

マクロ 説明
ICMP6_FILTER_SETPASSALL すべての ICMPv6 メッセージをアプリケーションに渡します。
ICMP6_FILTER_SETBLOCKALL すべての ICMPv6 メッセージをアプリケーションに渡さずブロックします。
ICMP6_FILTER_SETPASS 指定されたタイプの ICMPv6 メッセージをアプリケーションに渡します。
ICMP6_FILTER_SETBLOCK 指定されたタイプの ICMPv6 メッセージをアプリケーションに渡さずブロックします。
ICMP6_FILTER_WILLPASS 指定されたタイプのメッセージがアプリケーションに渡されるときに true を返します。
ICMP6_FILTER_WILLBLOCK 指定されたタイプのメッセージがアプリケーションに渡されずブロックされるときに true を返します。

設定済みのフィルタをクリアするには,ICMP_FILTER オプションに長さ 0 のフィルタを指定して setsockopt を呼び出します。 詳細については, icmp(7) を参照してください。

カーネルは,メッセージ・タイプ,メッセージの内容,パケット構造の正当性チェックは行いません。 このチェックは,アプリケーションが行う必要があります。

4.7.2.2    IPv6 ヘッダへのアクセス

IPv6 raw ソケットを使う場合,アプリケーションは IPv6 ヘッダの内容を受信できなければなりません。 このオプション情報を受信するには,対応するソケット・オプションを指定して setsockopt システム・コールを呼び出します。 表 4-11 に,オプション情報と,対応するソケット・オプションの一覧を示します。

表 4-11:  オプション情報とソケット・オプション

オプション情報 ソケット・オプション cmsg_type
デスティネーション IPv6 アドレスと受信インタフェース (受信の場合)。ソース IPv6 アドレスと送信インタフェース (送信の場合) IPV6_RECVPKTINFO IPV6_PKTINFO
ホップ限界値 IPV6_RECVHOPLIMIT IPV6_HOPLIMIT
ルーティング・ヘッダ IPV6_RECVRTHDR IPV6_RTHDR
ホップ・バイ・ホップ・オプション IPV6_RECVHOPOPTS IPV6_HOPOPTS
デスティネーション・オプション IPV6_RECVDSTOPTS IPV6_DSTOPTS

これらのソケット・オプションについての詳細は, ip(7) を参照してください。

recvmsg システム・コールは,受信したデータを,cmsghdr データ構造体内の 1 個以上の補助データ・オブジェクトとして返します。 この構造体についての詳細は,4.2.3.4 項を参照してください。

ソケット・オプションの値を調べるには,対応するオプションを指定して getsockopt システム・コールを呼び出します。 IPV6_RECVPKTINFO オプションが設定されていない場合,この関数は,ipi6_addr に in6addr_any が設定され,ipi6_addr に 0 が設定された状態で in6_pktinfo データ構造体を返します。 他のオプションについては,オプション値がない場合,この関数は option_len の値として 0 を返します。

アプリケーションは次の IPv6 ヘッダ情報を,入力パケットの補助データとして受信できます。

IPv6 アドレスとインタフェース・インデックスは,recvmsg システム・コールの補助データとして受信された in6_pktinfo データ構造体に含まれています。 in6_pktinfo データ構造体は,netinet/in.h に定義されています。

IPv6 アドレスの受信

IPV6_RECVPKTINFO オプションが有効の場合,recvmsg システム・コールは in6_pktinfo データ構造体を補助データとして返します。 ipi6_addr メンバには,受信パケットから取り出したデスティネーション IPv6 アドレスが入っています。 TCP ソケットの場合,デスティネーション・アドレスはコネクションのローカル・アドレスです。

インタフェースの受信

IPV6_RECVPKTINFO オプションが有効の場合,recvmsg システム・コールは in6_pktinfoデータ構造体を補助データとして返します。 ipi6_ifindex メンバには,パケットを受信したインタフェースのインタフェース・インデックスが入っています。

ホップ限界値の受信

IPV6_RECVHOPLIMIT オプションが有効の場合,recvmsg システム・コールは cmsghdr データ構造体を補助データとして返します。 cmsg_type メンバは IPV6_HOPLIMIT で,cmsg_data[] メンバには整数のホップ限界値の第 1 バイトが入っています。

4.7.2.3    IPv6 ルーティング・ヘッダへのアクセス

Advanced Sockets API を使うと,IPv6 ルーティング・ヘッダにアクセスできます。 ルーティング・ヘッダは IPv6 の拡張ヘッダであり,アプリケーションがソース・ルーティングを行えるようにします。 現在 RFC 2460 では,127 個までの中間ノード (128 ホップ) をサポートするタイプ 0 のルーティング・ヘッダが定義されています。

表 4-12 に,アプリケーションがルーティング・ヘッダの作成や調査に使うソケット呼び出しの一覧を示します。 以降この項では,ルーティング・ヘッダに関連する作業について説明します。

表 4-12:  ルーティング・ヘッダ用のソケット呼び出し

名前 説明
inet6_rth_space ルーティング・ヘッダに必要なバイト数を返します。
inet6_rth_init ルーティング・ヘッダのバッファ・データを初期化します。
inet6_rth_add ルーティング・ヘッダにアドレスを 1 つ追加します。
inet6_rth_reverse ルーティング・ヘッダ内のフィールドの順序を逆にします。
inet6_rth_segments ルーティング・ヘッダ内のセグメント数 (アドレスの数) を返します。
inet6_rth_getaddr ルーティング・ヘッダからアドレスを 1 つ取り出します。

ルーティング・ヘッダの受信

ルーティング・ヘッダを受信するには,アプリケーションは IPV6_RECVRTHDR オプションを有効にして setsockopt を呼び出します。 このオプションの使用例は, ip(7) を参照してください。

各受信ルーティング・ヘッダに対し,カーネルは補助データ・オブジェクトを 1 つ,cmsg_type メンバに IPV6_RTHDR が設定された cmsghdr 構造体に格納して渡します。 アプリケーションは,inet6_rth_reverseinet6_rth_segmentsinet6_rth_getaddr を呼び出して,ルーティング・ヘッダを処理します。 詳細については, inet6_rth_reverse(3)inet6_rth_segments(3),および inet6_rth_getaddr(3) を参照してください。

ルーティング・ヘッダの送信

ルーティング・ヘッダを送信するには,アプリケーションはルーティング・ヘッダを sendmsg 呼び出しの補助データとして指定するか,setsockopt を呼び出して指定します。 アプリケーションは,IPV6_RTHDR オプションの setsockopt を呼び出して,オプションの長さにゼロ (0) を指定することで,スティッキ・ルーティング・ヘッダを削除できます。

補助データを使うときには,アプリケーションは cmsg_level メンバに IPPROTO_IPV6 を設定し,cmsg_type メンバに IPV6_RTHDR を設定します。 inet6_rth_space 呼び出し,inet6_rth_init 呼び出し,および inet6_rth_add 呼び出しを使って,ルーティング・ヘッダを構築します。 詳細については, inet6_rth_space(3)inet6_rth_init(3),および inet6_rth_append(3) を参照してください。

アプリケーションがルーティング・ヘッダを指定するときには,connectsendto,または sendmsg の呼び出しで指定されたデスティネーション・アドレスが,データグラムの最終デスティネーションになります。 このため,ルーティング・ヘッダには,すべての中間ノードのアドレスが入っています。

RFC 2460 で規定されている拡張ヘッダの順序の都合上,アプリケーションは送信ルーティング・ヘッダを 1 つだけ指定できます。

4.7.2.4    IPv6 オプション・ヘッダへのアクセス

Advanced Sockets API を使うと,アプリケーションは次の IPv6 オプション・ヘッダにアクセスできます。

ヘッダの境界合わせの要件や拡張ヘッダの順序についての詳細は,RFC 2460 を参照してください。

表 4-13 に,ホップ・バイ・ホップ・オプション・ヘッダおよびデスティネーション・オプション・ヘッダの構築や調査にアプリケーションが使うソケット呼び出しの一覧を示します。 以降この項では,これらのオプション・ヘッダに関連する作業について説明します。

表 4-13:  オプション・ヘッダ用のソケット呼び出し

名前 説明
inet6_opt_init オプションのバッファ・データを初期化します。
inet6_opt_append オプション・ヘッダにオプションを追加します。
inet6_opt_finish オプション・ヘッダへのオプションの追加を終了します。
inet6_opt_set_val オプション・ヘッダにオプション・コンテントの構成要素を 1 個追加します。
inet6_opt_next オプション・ヘッダから次のオプションを取り出します。
inet6_opt_find オプション・ヘッダから,指定されたタイプのオプションを取り出します。
inet6_opt_get_val オプション・ヘッダから,オプション・コンテントの構成要素を 1 個取り出します。

ホップ・バイ・ホップ・オプションの受信

ホップ・バイ・ホップ・オプション・ヘッダを受信するには,アプリケーションは IPV6_RECVHOPOPTS オプションを有効にして setsockopt を呼び出します。 このオプションの使用例は, ip(7) を参照してください。

補助データを使う場合,カーネルはホップ・バイ・ホップ・オプション・ヘッダをアプリケーションに渡し,cmsg_level メンバに IPPROTO_IPV6 を設定し,cmsg_type メンバに IPV6_HOPOPTS を設定します。 アプリケーションは inet6_opt_nextinet6_opt_find,および inet6_opt_get_val を呼び出して,これらのオプションを取り出します。 詳細については, inet6_opt_next(3)inet6_opt_find(3),および inet6_opt_get_val(3) を参照してください。

ホップ・バイ・ホップ・オプションの送信

ホップ・バイ・ホップ・オプション・ヘッダを送信するには,アプリケーションはヘッダを sendmsg 呼び出しの補助データとして指定するか,setsockopt を呼び出して指定します。 アプリケーションは,IPV6_HOPOPTS オプションで setsockopt を呼び出して,オプションの長さにゼロ (0) を指定することで,スティッキ・ホップ・バイ・ホップ・オプション・ヘッダを削除できます。

補助データを使う場合,すべてのホップ・バイ・ホップ・オプションは,1 個の補助データ・オブジェクトで指定されます。 アプリケーションは,cmsg_level メンバに IPPROTO_IPV6 を設定し,cmsg_type メンバに IPV6_HOPOPTS を設定します。 inet6_opt_init 呼び出し, inet6_opt_append 呼び出し,inet6_opt_finish 呼び出し,および inet6_opt_set_val 呼び出しを使って,オプション・ヘッダを構築します。 詳細については, inet6_opt_init(3)inet6_opt_append(3)inet6_opt_finish(3),および inet6_opt_set_val(3) を参照してください。

デスティネーション・オプションの受信

デスティネーション・オプション・ヘッダを受信するには,アプリケーションは IPV6_RECVDSTOPTS オプションを有効にして setsockopt を呼び出します。 このオプションの使用例は, ip(7) を参照してください。 カーネルは,各デスティネーション・オプション・ヘッダを 1 個の補助データ・オブジェクトとしてアプリケーションに渡し,cmsg_level メンバに IPPROTO_IPV6 を設定し,cmsg_type メンバに IPV6_DSTOPTS を設定します。

アプリケーションは,inet6_opt_nextinet6_opt_find,および inet6_opt_get_val を呼び出して,これらのオプションを処理します。 詳細については, inet6_opt_next(3)inet6_opt_find(3),および inet6_opt_get_val(3) を参照してください。

デスティネーション・オプションの送信

デスティネーション・オプション・ヘッダを送信するには,アプリケーションはヘッダを sendmsg 呼び出しの補助データとして指定するか,setsockopt を呼び出して指定します。 アプリケーションは,IPV6_DSTOPTS オプションで setsockopt を呼び出して,オプションの長さにゼロ (0) を指定することで,スティッキ・デスティネーション・オプション・ヘッダを削除できます。

RFC 2460 に従い,API は拡張ヘッダが順番に並んでいることを前提としています。 デスティネーション・オプションは,ルーティング・ヘッダの前に 1 セットだけと,ルーティング・ヘッダの後ろに 1 セットだけ置くことができます。 各セットには 1 個以上のオプションを含めることができますが,各セットは 1 個の拡張ヘッダと見なされます。

ルーティング・ヘッダの後ろのデスティネーション・オプションで補助データを使う場合や,ルーティング・ヘッダが指定されていない場合,アプリケーションは cmsg_level メンバに IPPROTO_IPV6 を設定し,cmsg_type メンバに IPV6_DSTOPTS を設定します。

アプリケーションは inet6_opt_initinet6_opt_appendinet6_opt_finish,および inet6_opt_set_val を呼び出して,これらのオプションを構築します。 詳細については, inet6_opt_init(3)inet6_opt_append(3)inet6_opt_finish(3),および inet6_opt_set_val(3) を参照してください。

4.7.3    特定のプロトコルの選択

socket システム・コールの構文は,4.3.1 項で説明しています。 socket システム・コールの 3 番目の引数の protocol 引数がゼロ (0) である場合, socket システム・コールは,省略時のプロトコルを選択して,返されたソケット記述子と一緒に使用します。 省略時のプロトコルは通常適切であるので,必ずしも代わりのプロトコルを選択できるとは限りません。 ただし,raw ソケットを使用して,低レベルのプロトコルまたはハードウェア・インタフェースと直接通信するには,プロトコル引数を必ず使用して,多重分離をセットアップします。

たとえば,インターネット・ファミリの raw ソケットを使用して,IP の上に新しいプロトコルをインプリメントすることができます。 この場合,そのソケットは,指定されたプロトコルのパケットだけを受信します。 特定のプロトコルを使用するには,通信ドメイン内で定義されているプロトコル番号を確認しなければなりません。 インターネット・ドメインでは,4.2.3.2 項で説明しているライブラリ・ルーチンの1つを使用することができます。

次のコードは,getprotobyname ライブラリ・コールを使用して,インターネット・ドメインでオープンする SOCK_STREAM ソケットのプロトコル newtcp を選択する方法を示しています。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
  .
  .
  .
struct protoent *pp;
  .
  .
  .
pp = getprotobyname("newtcp");
s = socket(AF_INET, SOCK_STREAM, pp->p_proto);
 

4.7.4    名前とアドレスのバインド

bind システム・コールは,アドレスをソケットに関連付けます。

4.7.4.1    ワイルドカード・アドレスへのバインド

ローカル・マシンのソケットのアドレスには,そのマシンの任意の有効なネットワーク・アドレスを指定することができます。 1 つのシステムは,有効なネットワーク・アドレスを複数持つことができるので,インターネット・ドメインでソケットにアドレスをバインドすると複雑になることがあります。 ローカル・アドレスのバインドを簡単にするために,ワイルドカード・アドレスである,定数 INADDR_ANY (AF_INET) と in6addr_any (AF_INET6) が用意されています。 アドレスが複数ある場合,ワイルドカード・アドレスは,このサーバ・プロセスが,どのインターネット・インタフェースの接続でも受け入れることをシステムに示します。

次の例は,ワイルドカード値 INADDR_ANY をローカル・ソケットにバインドする方法を示しています。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
 
main()
{
   int s, length;
   struct sockaddr_in name;
   char buf[1024];
  .
  .
  .
   /* Create name with wildcards. */
   name.sin_family = AF_INET;
   name.sin_len = sizeof(name);
   name.sin_addr.s_addr = INADDR_ANY;
   name.sin_port = 0;
   if (bind(s, (struct sockaddr *)&name, sizeof(name))== -1) {
      perror("binding datagram socket");
      exit(1);
   }
  .
  .
  .
}
 

ワイルドカードのローカル・アドレスを持つソケットは,指定されたポート番号宛のメッセージを受信でき,該当するホストに割り当てられた使用可能なアドレスのうちの任意のアドレスに対してメッセージを送信できます。 ソケットは,ローカル・アドレスにワイルドカード値を使用しますが,指定のソケットにメッセージを送信するプロセスは,有効なネットワーク・アドレスを指定しなければならないことに注意してください。 プロセスはどこからのメッセージでも受信できますが,どこへでも送信できるわけではありません。

AF_INET ソケットはシステム上の IPv4 アドレスを指定するメッセージだけを受信できます。 しかし,AF_INET6 ソケットはシステム上の IPv4 アドレス,もしくは IPv6 アドレスを指定して送信されたメッセージのどちらでも受信できます。 AF_INET6 ソケットは,IPv4 アドレス表現するのに IPv4 にマップされた IPv6 アドレス形式を使用しています。 IPv6 アドレスについては,『ネットワーク管理ガイド:接続編』を参照してください。

複数のネットワーク・インタフェースを持つシステム上のサーバ・プロセスで,そのインタフェース・アドレスの 1 つだけとホストが接続できるようにする場合には,サーバ・プロセスは,該当するインタフェースのアドレスをバインドします。 たとえば,システムに,130.180.123.45 と 131.185.67.89 の 2 つのアドレスがある場合,サーバ・プロセスは,アドレス 130.180.123.45 をバインドすることができます。 このアドレスをバインドすると,130.180.123.45 への接続だけがサーバ・プロセスに接続できるようになります。

同様に,ローカル・ポートは,指定しないまま (ゼロを指定する) にしておくことができます。 この場合には,システムによって,ポート番号が選択されます。

4.7.4.2    UNIX ドメインにおけるバインド

UNIX ドメイン (AF_UNIX) で通信しているプロセスは,ローカル・パス名および外部パス名を含んだ関連付けによってバインドされます。 UNIX ドメインのソケットには,名前をバインドする必要はありません。 ただし,名前をバインドすれば,プロトコル,ローカル・パス名,または外部パス名のバインドが重複することがなくなります。 パス名は,システムに存在しているファイルを参照することはできません。 名前をソケットにバインドするプロセスは,バインドされたソケットが入るディレクトリに対する書き込み許可がなければなりません。

次の例は,名前 socket を,UNIX ドメインで作成されたソケットにバインドする方法を示しています。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
 
#define NAME "socket"
 
main()
{
   int s, length;
   struct sockaddr_un name;
   char buf[1024];

.
.
.
/* Create name. */ name.sun_len = sizeof(name.sun_len) + sizeof(name.sun_family) + strlen(NAME); name.sun_family = AF_UNIX; strcpy(name.sun_path, NAME); if (bind(s, (struct sockaddr *) &name, sizeof(name))==-1) { perror("binding name to datagram socket"); exit(1); }
.
.
.
}  

4.7.5    帯域外データ

帯域外データは,接続されたストリーム・ソケットの各ペアに関連付けられた,論理的に独立している伝送チャネルです。 帯域外データは,setsockopt システム・コールを使用して設定される SO_OOBINLINE オプションの状態に応じて,通常の受信キューとは無関係に,または受信キュー内で,ソケットに引き渡すことができます。

ストリーム・ソケットの抽象表現では,帯域外データ処理機能は,少なくとも一度に 1 つは高い信頼性で帯域外メッセージの引き渡しを行わなければならないことが指定されています。 このメッセージには,1 バイト以上のデータが入っていなければなりません。 また,どの時点でも,1 つ以上のメッセージのユーザへの引き渡しを保留できます。

ソケット層は,緊急データまたは帯域外処理の終わりを示す,データ・ストリーム内のマークをサポートします。 ソケット・メカニズムでは,1 つのシステム・コールでマークの両側からのデータを返しません。

MSG_PEEK を使用すると,帯域外データの中身を見ることができます。 ソケットにプロセス・グループがある場合は,プロトコルにその存在が通知されたときに,SIGURG シグナルが生成されます。 プロセスは,プロセス・グループまたはプロセス ID を,適切な fcntl コールにより SIGURG シグナルを使用して通知されるように設定できます。 SIGIO については,4.7.10 項で説明します。

複数のソケットに,引き渡しを待つ帯域外データがある場合,アプリケーション・プログラムは,select システム・コールを使用して例外的な条件を調べ,そのような保留中のデータを持つソケットを判別できます。 SIGURG シグナルまたは select システム・コールは,アプリケーション・プログラムに,データが保留中であることを通知します。 この場合,アプリケーションは,適切な呼び出しを発行して,実際にデータを受信しなければなりません。

引き渡された情報に加えて,データ・ストリームに論理マークが置かれ,帯域外データが送信されたポイントを示します。 シグナルが保留中の出力をフラッシュすると,データ・ストリーム内の論理マークの前までのすべてのデータが破棄されます。

帯域外メッセージを送信するには,send または sendto のシステム・コールに MSG_OOB フラグをセットします。 帯域外データを受信するには,recvfrom または recv のシステム・コールを実行するときに,アプリケーション・プログラムで MSG_OOB フラグをセットしなければなりません。

アプリケーション・プログラムは,次の ioctl の SIOCATMARK を使用することによって, 読み取りポインタが,データ・ストリーム内のマークを現在参照しているかどうかを判別できます。

ioctl(s, SIOCATMARK, &yes);
 

この呼び出しが戻ったとき yes が 1 の場合は,帯域外データが到着していないことを示し,次の読み取りでは,マークより後のデータが返されます。 帯域外データが到着している場合には,次の読み取りで,帯域外シグナルを伝送する前にクライアントから送信されたデータが返されます。 次のプログラムは,リモート・ログイン・プロセスで使用して,割り込みまたは終了シグナルの受信時に出力をフラッシュするルーチンです。 このプログラムは,通常データをマークの前まで (破棄するために) 読み取ってから,帯域外バイトを読み取ります。

#include <sys/ioctl.h>
#include <sys/file.h>

.
.
.
oob() { int out = FWRITE, mark; char waste[BUFSIZ];   /* flush local terminal output */ ioctl(1, TIOCFLUSH, (char *)&out); for (;;) { if (ioctl(rem, SIOCATMARK, &mark) < 0) { perror("ioctl"); break; } if (mark) break; (void) read(rem, waste, sizeof (waste)); } if (recv(rem, &mark, 1, MSG_OOB) < 0) { perror("recv");
.
.
.
} }  

プロセスでは,論理マークより前のデータを先に読み取らなくても,帯域外データを読み取ったり,中身を見ることができます。 これは,基礎となるプロトコルが,緊急帯域内データを通常データとともに引き渡し,その存在を示す通知だけを前もって送信する場合には困難です。 たとえば,TCP プロトコルがそうです。 そのようなプロトコルでは,帯域外バイトが到着していない場合に,recv システム・コールが MSG_OOB フラグをセットして実行されると,このシステム・コールは,EWOULDBLOCK エラーを返します。 入力バッファに帯域内データが十分入っていると,通常のフロー制御は,バッファがクリアされるまで,対等プロセスが緊急データを送信するのを妨げることがあります。 このとき,プロセスは,キューに入ったデータを十分に読み取って,緊急データが引き渡せるようにしなければなりません。

注意

プログラムの中には,緊急データの複数バイトを使用して,複数の緊急シグナルを処理しなければならないものがあります。 このようなプログラムでは,ストリーム内の緊急データの位置を保持する必要があります。 ソケット・レベルの SO_OOBINLINE オプションがこの機能を提供しているので,できるだけこのオプションを使用してください。

ソケット・レベルの SO_OOBINLINE オプションは,緊急データの位置 (論理マーク) を保持しす。 緊急データは,MSG_OOB フラグなしで返された通常データ・ストリーム内で,マークの直後に続きます。 複数の緊急指示を受信するとマークは移動しますが,帯域外データは失われません。

4.7.6    インターネット・プロトコル・マルチキャスト

インターネット・プロトコル (IP)・マルチキャストは,アプリケーションがイーサネットおよび Fiber Distribution Data Interface (FDDI) ネットワークのマルチキャスト機能に IP 層アクセスを行うことができるようにします。 IP マルチキャストはデータグラムを効率よく引き渡そうとするため,IP ブロードキャスト (4.7.7 項で説明) が関係のないホストに課すオーバヘッドを避けます。 IP マルチキャストはまた,アプリケーションによるネットワーク帯域幅の消費もありません。 IP マルチキャストを使用しない場合,アプリケーションは同一データの入っている別々のパケットを複数のデスティネーションに伝送します。

IPv4 マルチキャストは,マルチキャスト・グループを使用することにより,複数ホストへの配信を効率よく行います。 マルチキャスト・グループは,単一のクラス D IP デスティネーション・アドレス (IPv4) または単一のマルチキャスト・アドレス (IPv6) によって識別される 0 個以上のノードから構成されるグループです。 IPv4 クラス D アドレスの上位 4 ビットは 1110 です。 ドット表記 10 進数では,IP マルチキャスト・アドレスは 224.0.0.0 から 239.255.255.255 までの範囲ですが,224.0.0.0 は予約されています。 IPv6 マルチキャスト・アドレスのフォーマットには,プレフィックス FF00::/8 が付いています。

あるマルチキャスト・グループのメンバは,そのマルチキャスト・グループを表す IP アドレスに送信されたすべてのデータのコピーを受信します。 マルチキャスト・グループは永久的なものでも一時的なものでもかまいません。 永久的なグループは,管理用に割り当てられた周知の IP アドレスを持ちます。 永久的なグループで,永久的なものはアドレスであり,メンバではありません。 グループのメンバ数は変化し,ゼロになることもあります。

IPv4 では,all hosts というグループは永久的なホスト・グループの例であり,割り当てられているアドレスは 224.0.0.1 です。 Tru64 UNIX システムは,インターネット・グループ管理プロトコル (IGMP) に加わるために all hosts group に加わります。 IGMP および IP マルチキャストについての詳細は,RFC 1112:『Host Extensions for IP Multicastingを参照してください。

IPv6 では,All Nodes マルチキャスト・アドレスは,アドレスが FF01::1 (ノード・ローカル,つまりスコープ 1) および FF02::1 (リンク・ローカル,つまりスコープ 2) の永久的なグループの一例です。 IPv6 マルチキャスト・アドレスについての詳細は,RFC 1884: 『IPv6 Addressing Architectureを参照してください。

永久的なマルチキャスト・グループのために予約されていない IP アドレスは,一時的なグループに動的に割り当てることができます。 一時的なグループは 1 つ以上のメンバを持つときのみ存在します。

注意

IP マルチキャストは,TCP などのコネクション指向型トランスポートではサポートされていません。

IP マルチキャストは,次の項で説明するように,setsockopt システム・コールにオプションを使用してインプリメントされます。 マルチキャストに関係するソケット・オプションに必要な定義は <netinet/in.h> にあります。 IP マルチキャスト・データグラムを受信するアプリケーションには,このヘッダ・ファイルをインクルードしなければなりません。

4.7.6.1    IPv4 マルチキャスト・データグラムの送信

IPv4 マルチキャスト・データグラムを送信するには,sendto システム・コールにおいて,224.0.0.0 から 239.255.255.255 の範囲で IP デスティネーション・アドレスを指定することにより,送信先のホスト・グループを示します。 システムはデータグラムを伝送する前に,指定された IP デスティネーション・アドレスを適切なイーサネットまたは FDDI マルチキャスト・アドレスにマップします。

アプリケーションは,setsockopt システム・コールに引数を指定することにより,マルチキャスト・オプションを明示的に制御できます。 次のオプションは,setsockopt システム・コールを使用して,アプリケーションで設定できます。

注意

setsockopt システム・コールの構文および引数については,4.3.5 項および setsockopt(2) に説明があります。 この項および4.7.6.2 項の例は,IPv4 マルチキャスト・データグラムだけに適用される setsockopt オプションの使用方法を示しています。

setsockopt システム・コールの IP_MULTICAST_TTL オプションを使用すると,アプリケーションは time-to-live (TTL) フィールドに対して 0 〜 255 の値を指定することができます。 TTL 値が 0 のマルチキャスト・データグラムは,ローカル・ホスト上で実行しているアプリケーションへのマルチキャスト・データグラムの配信を制限します。 TTL 値が 1 のマルチキャスト・データグラムは,ローカル・サブネット上のホストのみに転送されます。 マルチキャスト・データグラムの TTL 値が 1 より大きく,送信側のホストのネットワークにマルチキャスト・ルータが接続されている場合には,マルチキャスト・データグラムはローカル・サブネットを越えて転送されます。 マルチキャスト・ルータは,指定されたマルチキャスト・グループに属するホストを持つ既知のネットワークにデータグラムを転送します。 TTL 値はパスの各マルチキャスト・ルータでカウント・ダウンされます。 TTL 値が 0 になると,データグラムはそれ以上転送されません。

次の例は,setsockopt システム・コールの IP_MULTICAST_TTL オプションの使用方法を示しています。

u_char ttl;
ttl=2;
 
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
                                   sizeof(ttl)) == -1)
				   perror("setsockopt");
 

IP マルチキャスト・デスティネーション宛のデータグラムは,アプリケーションで代替ネットワーク・インタフェースがソケットに関連付けられていることを指定していなければ,省略時のネットワーク・インタフェースから伝送されます。 省略時のインタフェースは,カーネル・ルーティング・テーブルの省略時の経路に関連付けられているインタフェース, または,明示的な経路がある場合には,それに関連付けられているインタフェースによって決定されます。 setsockopt システム・コールで IP_MULTICAST_IF オプションを使用すると,アプリケーションは,カーネル・ルーティング・テーブルの経路で指定されているネットワーク・インタフェース以外のものを指定することができます。

次の例は,setsockopt システム・コールの IP_MULTICAST_IF オプションを使用して,省略時のインタフェース以外のインタフェースを指定する方法を示しています。

int sock;
struct in_addr ifaddress;
char *if_to_use = "16.141.64.251";

.
.
.
ifaddress.s_addr = inet_addr(if_to_use); if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &ifaddress, sizeof(ifaddress)) == -1) perror ("error from setsockopt IP_MULTICAST_IF"); else printf ("new interface set for sending multicast datagrams\n");    

送信側のホストが属しているグループへマルチキャスト・データグラムを送信する場合,省略時にはデータグラムのコピーが IP 層によりローカル引き渡しのためにループ・バックされます。 setsockopt システム・コールの IP_MULTICAST_LOOP オプションを使用すると,アプリケーションはこのループバック引き渡しを使用不能にすることができます。

次の例は,setsockopt システム・コールの IP_MULTICAST_LOOP オプションの使用方法を示しています。

u_char loop=0;
if (setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, &loop
         sizeof(loop)) == -1)
 perror("setsockopt");
 

loop の値が 0 の場合,ループバックは使用不能です。 loop の値が 1 の場合には使用可能です。 性能上の理由により,同じホストのアプリケーションがデータグラムのコピーを受信する必要がある場合を除いて,省略時の動作 (使用可能) から使用不能に変更することをお勧めします。

4.7.6.2    IPv4 マルチキャスト・データグラムの受信

all hosts group 以外の特定のマルチキャスト・グループ宛の IP マルチキャスト・データグラムをホストが受信できるようにするために,アプリケーションはホストにそのマルチキャスト・グループのメンバになるように指示しなければなりません。 この項では,アプリケーションがホストに指示して,マルチキャスト・グループに加わったり抜けたりする方法を説明します。

アプリケーションは,setsockopt システム・コールの IP_ADD_MEMBERSHIP オプションを使用すると,そのアプリケーションを実行しているホストにマルチキャスト・グループに加わるように指示することができます。

struct ip_mreq mreq;
if (setsockopt( sock, IPPROTO_IP, IP_ADD_MULTICAST, &mreq
         sizeof(mreq)) == -1)
         perror("setsockopt");
 

mreq 変数の構造体を次に示します。

structip_mreq{
        struct in_addr imr_multiaddr; /* IP multicastaddress of group */
        struct in_addr imr_interface; /* local IPaddress of interface */
        };

各マルチキャスト・グループのメンバシップは,特定のインタフェースと関連付けられています。 複数のインタフェース上にある同一のグループに加わることができます。 imr_interface 変数を INADDR_ANY と指定すると,アプリケーションは省略時のマルチキャスト・インタフェースを選択することができます。 もう 1 つの方法として,ホストのローカル・アドレスのいずれかを指定すると,アプリケーションは特定のマルチキャスト可能なインタフェースを選択することができます。 単一のソケットに加えることができるメンバシップの最大数は,<netinet/in.h> ヘッダ・ファイルに定義されている IP_MAX_MEMBERSHIPS の値によって決まります。

特定のマルチキャスト・グループのメンバシップを除くには,setsockopt システム・コールの IP_DROP_MEMBERSHIP オプションを使用します。

struct ip_mreq mreq;
if (setsockopt( sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq
         sizeof(mreq))== -1)
 perror("setsockopt");
 

mreq 変数には,メンバシップを加えるときと同じ構造体の値が入ります。

あるホストを特定のマルチキャスト・グループに加えることを複数のソケットが要求した場合には,それらのソケットのうちの最後のソケットがクローズされるまで,そのホストはマルチキャスト・グループのメンバのままになります。

特定の UDP ポートに送信されたマルチキャスト・データグラムを受信するには,受信側のソケットは bind システム・コールを使用してそのポートにバインドされていなければなりません。 bind システム・コール (4.3.2 項を参照) の前に SO_REUSEPORT オプションを指定する setsockopt システム・コールが発行されている場合には,同一のポート宛の UDP データグラムを複数のプロセスが受信できます。 次の例は,setsockopt システム・コールで SO_REUSEPORT オプションを使用する方法を示しています。

int setreuse = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &setreuse,
                              sizeof(setreuse)) == -1)
                     perror("setsockopt");
 

SO_REUSEPORT オプションが設定されている場合,共用ポート宛のすべての着信マルチキャストまたはブロードキャスト UDP データグラムは,そのポートにバインドされているすべてのソケットに引き渡されます。

IP マルチキャスト・データグラムの SOCK_RAW ソケットへの引き渡しは,デスティネーションのプロトコル・タイプによって決まります。

4.7.6.3    IPv6 マルチキャスト・データグラムの送信

IPv6 マルチキャスト・データグラムを送信するには,sendto システム・コールで IPv6 マルチキャスト・アドレスを指定することにより,送信先のマルチキャスト・グループを示します。 システムはデータグラムを伝送する前に,指定された IPv6 デスティネーション・アドレスを適切なイーサネットまたは FDDI マルチキャスト・アドレスにマップします。

アプリケーションは,setsockopt システム・コールに引数を指定することにより,マルチキャスト・オプションを明示的に制御できます。 次のオプションは,setsockopt システム・コールを使用して,アプリケーションで設定できます。

注意

setsockopt システム・コールの構文および引数については,4.3.5 項および setsockopt(2) に説明があります。 この項および4.7.6.4 項の例は,IPv6 マルチキャスト・データグラムだけに適用される setsockopt オプションの使用方法を示しています。

setsockopt システム・コールの IPV6_MULTICAST_HOPS オプションを使用すると,アプリケーションはホップ限界値のフィールドに対して 0 〜 255 の値を指定することができます。 ホップ限界値が 0 のマルチキャスト・データグラムは,ローカル・ホスト上で実行しているアプリケーションへのマルチキャスト・データグラムの配信を制限します。 ホップ限界値が 1 のマルチキャスト・データグラムは,ローカル・リンク上のホストのみに転送されます。 マルチキャスト・データグラムのホップ限界値が 1 より大きく,送信側のホストのネットワークにマルチキャスト・ルータが接続されている場合には,マルチキャスト・データグラムをローカル・リンク外へ転送することができます。 マルチキャスト・ルータは,指定されたマルチキャスト・グループに属するホストを持つ既知のネットワークにデータグラムを転送します。 ホップ限界値はパスの各マルチキャスト・ルータでカウント・ダウンされます。 ホップ限界値が 0 になると,データグラムはそれ以上転送されません。

次の例は,setsockopt システム・コールの IPV6_MULTICAST_HOPS オプションの使用方法を示しています。

int hops;
hops=2;
 
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
                                   sizeof(hops)) < 0)
				   perror("setsockopt: IPV6_MULTICAST_HOPS error");
 

IPv6 マルチキャスト・アドレス宛のデータグラムは,アプリケーションで代替ネットワーク・インタフェースがソケットに関連付けられていることを指定していなければ,省略時のネットワーク・インタフェースから伝送されます。 省略時のインタフェースは,カーネル・ルーティング・テーブルの省略時の経路に関連付けられているインタフェース,または明示的な経路がある場合には,それに関連付けられているインタフェースによって決定されます。 setsockopt システム・コールで IPV6_MULTICAST_IF オプションを使用すると,アプリケーションは,カーネル・ルーティング・テーブルの経路で指定されているネットワーク・インタフェース以外のものを指定することができます。

次の例は,setsockopt システム・コールの IPV6_MULTICAST_IF オプションを使用して,省略時のインタフェース以外のインタフェースを指定する方法を示しています。

u_int if_index = 1;

.
.
.
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0) perror ("setsockopt: IPV6_MULTICAST_IF error"); else printf ("new interface set for sending multicast datagrams\n");

if_index パラメータは,必要なインタフェースのインタフェース・インデックスを指定するか,0 を指定して省略時のインタフェースを選択します。 if_nametoindex(3) を使用すると,インタフェース・インデックスを見つけることができます。

送信側のノードが属しているグループへマルチキャスト・データグラムを送信する場合,省略時にはデータグラムのコピーが IP 層によりローカル引き渡しのためにループ・バックされます。 setsockopt システム・コールの IPV6_MULTICAST_LOOP オプションを使用すると,アプリケーションはこのループバック引き渡しを使用不能にすることができます。

次の例は,setsockopt システム・コールの IPV6_MULTICAST_LOOP オプションの使用方法を示しています。

u_char loop=0;
if (setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop,
         sizeof(loop)) < 0)
         perror("setsockopt: IPV6_MULTICAST_LOOP error");

loop の値が 0 の場合,ループバックは使用不能です。 loop の値が 1 の場合には使用可能です。 性能上の理由により,同じホストのアプリケーションがデータグラムのコピーを受信する必要がある場合を除いて,省略時の動作 (使用可能) から使用不能に変更することをお勧めします。

4.7.6.4    IPv6 マルチキャスト・データグラムの受信

All Nodes グループ以外の特定のマルチキャスト・グループ宛の IPv6 マルチキャスト・データグラムをノードが受信できるようにするために,アプリケーションはホストにそのマルチキャスト・グループのメンバになるように指示しなければなりません。 この項では,アプリケーションがノードに指示して,マルチキャスト・グループに加わったり抜けたりする方法を説明します。

アプリケーションは,setsockopt システム・コールの IPV6_JOIN_GROUP オプションを使用すると,そのアプリケーションを実行しているノードにマルチキャスト・グループに加わるように指示することができます。

struct ipv6_mreq imr6;

.
.
.
imr6.ipv6mr_interface = if_index; if (setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&imr6, sizeof(imr6)) < 0) perror("setsockopt: IPV6_JOIN_GROUP error");

imr6 変数の構造体を次に示します。

structipv6_mreq {
        struct in6_addr ipv6mr_multiaddr; /*IP multicast address of group*/
        unsigned int ipv6mr_interface; /*local interface index*/
        };

各マルチキャスト・グループのメンバシップは,特定のインタフェースと関連付けられています。 複数のインタフェース上にある同一のグループに加わることができます。 ipv6mr_interface 変数に値 0 を指定すると,アプリケーションは省略時のマルチキャスト・インタフェースを選択することができます。 もう 1 つの方法として,ホストのローカル・インタフェースのいずれかを指定すると,アプリケーションは特定のマルチキャスト可能なインタフェースを選択することができます。 ipv6mr_multiaddr 変数は,IPv6 マルチキャスト・アドレスまたは IPv4 射影 IPv6 アドレスとして指定できます。

単一のソケットに加えることができるメンバシップの最大数は,<netinet/in.h> ヘッダ・ファイルに定義されている IPV6_MAX_MEMBERSHIPS の値によって決まります。

特定のマルチキャスト・グループのメンバシップを除くには,setsockopt システム・コールの IPV6_LEAVE_GROUP オプションを使用します。

struct ipv6_mreq imr6;
if (setsockopt( sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &imr6,
     sizeof(imr6)) < 0)
     perror("setsockopt: IPV6_LEAVE_GROUP error");

imr6 パラメータには,メンバシップを加えるときと同じ構造体の値が入ります。 このアドレスは,IPv6 マルチキャスト・アドレス,またはIPv4 射影 IPv6 アドレスとすることができます。

あるノードを特定のマルチキャスト・グループに加えることを複数のソケットが要求した場合には,それらのソケットのうちの最後のソケットがクローズされるまで,そのノードはマルチキャスト・グループのメンバのままになります。

特定の UDP ポートに送信されたマルチキャスト・データグラムを受信するには,受信側のソケットは bind システム・コールを使用してそのポートにバインドされていなければなりません。 bind システム・コール (4.3.2 項を参照) の前に SO_REUSEPORT オプションを指定する setsockopt システム・コールが発行されている場合には,同一のポート宛の UDP データグラムを複数のプロセスが受信できます。 このオプションの使用例については,4.7.6.2 項を参照してください。

IP マルチキャスト・データグラムの SOCK_RAW ソケットへの引き渡しは,デスティネーションのプロトコル・タイプによって決まります。

4.7.7    ブロードキャストとネットワーク構成の調査

データグラム・ソケットを使用すると,システムがサポートするさまざまなネットワーク上で,ブロードキャスト・パケットを送信することができます。 ネットワーク自身はブロードキャストをサポートしなければなりませんが,システムでは,ソフトウェアでのブロードキャストのシミュレーションを提供していません。

ブロードキャスト・メッセージは高い負荷をネットワークに与えることになります。 これは,ネットワーク上のすべてのホストがブロードキャストを処理しなければならないためです。 そのため,ブロードキャスト・パケットを送信する能力は,明示的に許可されているソケットに限定されます。

ブロードキャストは,通常,次の 2 つのうちいずれかの理由で使用されます。 つまり,リソースのアドレスが分からない状態でローカル・ネットワークのリソースを探すため,またはすべてのアクセス可能な他のホストに情報を送信するためです。

注意

ブロードキャストは,TCP のようなコネクション指向型トランスポートではサポートされていません。

ブロードキャスト・メッセージを送信するには,次の手順に従います。

  1. データグラム・ソケットを作成する。

    たとえば,次のように作成します。

    s = socket(AF_INET, SOCK_DGRAM, 0);
     
    

  2. ソケットをブロードキャスト用に指定する。

    たとえば,次のようになります。

    int   on = 1;
     
    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on,
                   sizeof(on)) == -1)
        perror("setsockopt");
     
    

  3. 少なくとも 1 つのポート番号を,必ずソケットにバインドする。

    sin.sin_len = sizeof(sin);
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(MYPORT);
    if (bind(s, (struct sockaddr *) &sin, sizeof (sin)) == -1)
        perror("setsockopt");
     
    

メッセージのデスティネーション・アドレスは,ブロードキャストされる先のネットワークによって異なります。 インターネット・ドメインでは,ブロードキャストの簡略表記法をローカル・ネットワークでサポートします。 そのアドレスは,netinet/in.h に定義されているように,INADDR_BROADCAST です。

すべてのホストに送信できるようにアドレスのリストを決定するには,ホストが接続するネットワークの情報が必要です。 オペレーティング・システムでは,システム・データ構造からのこの情報を検索する方法を提供しています。 ioctl 呼び出しの SIOCGIFCONF は,ホスト側のインタフェースの構成を単一の ifconf 構造体の形で返します。 この構造体は,ifreq 構造体の配列を収めたデータ領域を持ちます。 ifreq 構造体は,ホストが接続する各ネットワーク・インタフェースに対して 1 つです。 この構造体は,<net/if.h> ヘッダ・ファイルで次のように定義されています。

struct  ifconf {
   int     ifc_len;                /* size of associated buffer */
   union {
      caddr_t ifcu_buf;
      struct  ifreq *ifcu_req;
   } ifc_ifcu;
#define ifc_buf ifc_ifcu.ifcu_buf  /* buffer address */
#define ifc_req ifc_ifcu.ifcu_req  /* array of structures returned */
};
 
struct  ifreq {
#define IFNAMSIZ        16
   char    ifr_name[IFNAMSIZ];     /* if name, e.g. "ee0" */
   union {
      struct  sockaddr ifru_addr;
      struct  sockaddr ifru_dstaddr;
      struct  sockaddr ifru_broadaddr;
      short   ifru_flags;
      int     ifru_metric;
      caddr_t ifru_data;
   } ifr_ifru;
#define ifr_addr      ifr_ifru.ifru_addr      /* address */
#define ifr_dstaddr   ifr_ifru.ifru_dstaddr   /* other end of */
                                              /* p-to-p link */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_flags     ifr_ifru.ifru_flags     /* flags */
#define ifr_metric    ifr_ifru.ifru_metric    /* metric */
#define ifr_data      ifr_ifru.ifru_data      /* for use by */
                                              /* interface */
};
 

インタフェース構成を取得する実際の呼び出しは,次のようになります。

struct ifconf ifc;
char buf[BUFSIZ];
 
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {

.
.
.
}  

この呼び出しの後,ホストが接続する各ネットワークに対して 1 つの ifreq 構造体が,buf に設定されます。 そして,ifc.ifc_len が,ifreq 構造体で使用するバイト数を反映するように変更されます。

各構造体には,インタフェース・フラグのセットがあり,そのフラグに対応するネットワークが起動しているか停止しているか,二地点間またはブロードキャストか,などを示します。 ioctl の SIOCGIFFLAGS は,フラグを検索して,ifreq 構造体で指定されるインタフェースがないかどうかを,次のように調べます。

struct ifreq *ifr;
 
ifr = ifc.ifc_req;
 
for (n = ifc.ifc_len / sizeof (struct ifreq); --n >= 0; ifr++) {
   /*
    * We must be careful that we don't use an interface
    * devoted to an address family other than those intended.
    */
   if (ifr->ifr_addr.sa_family != AF_INET)
      continue;
   if (ioctl(s, SIOCGIFFLAGS, (char *) ifr) < 0) {

.
.
.
} /* * Skip irrelevant cases. */ if ((ifr->ifr_flags & IFF_UP) == 0 || (ifr->ifr_flags & IFF_LOOPBACK) || (ifr->ifr_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) == 0) continue;  

フラグを取得すると,ブロードキャスト・アドレスも取得しなければなりません。 ブロードキャスト・ネットワークの場合,これは ioctl の SIOCGIFBRDADDR を介して行われます。 二地点間ネットワークでは,デスティネーション・ホストのアドレスは,SIOCGIFBRDADDR を使用して取得します。 たとえば,次のように使用します。

struct sockaddr dst;
 
if (ifr->ifr_flags & IFF_POINTOPOINT) {
   if (ioctl(s, SIOCGIFDSTADDR, (char *) ifr) < 0) {
      ...
   }
   bcopy((char *) ifr->ifr_dstaddr, (char *) &dst,
      sizeof (ifr->ifr_dstaddr));
} else if (ifr->ifr_flags & IFF_BROADCAST) {
   if (ioctl(s, SIOCGIFBRDADDR, (char *) ifr) < 0) {
      ...
   }
   bcopy((char *) ifr->ifr_broadaddr, (char *) &dst,
      sizeof (ifr->ifr_broadaddr));
}
 

適切な ioctl システム・コールにより,ブロードキャスト・アドレスまたはデスティネーション・アドレス (dst に格納される) を取得した後に,sendto システム・コールを使用します。 たとえば,次のように使用します。

if (sendto(s, buf, buflen, 0, (struct sockaddr *)&dst, sizeof (dst)) < 0)
    perror("sendto");
 

前述のループでは,ホストが接続し,ブロードキャストおよび二地点間のアドレス指定をサポートするすべてのインタフェースに対して,sendto システム・コールが一度実行されます。 ブロードキャスト・メッセージを所定のネットワークに転送するだけのプロセスの場合は,前述の例で示したようにコーディングします。 ただし,適切なデスティネーション・アドレスを見つけるループ処理が必要になります。

4.7.8    inetd デーモン

オペレーティング・システムでは inetd インターネット・スーパサーバ・デーモンをサポートしています。 この inetd デーモンは,ブート時に呼び出され,/etc/inetd.conf ファイルを読み込んでリッスンするサーバを決定します。

注意

Tru64 UNIX では,ソケット上で実行するように作成されているサーバ・アプリケーションのみが inted デーモンを使用できます。 Tru64 UNIX の inetd デーモンは,STREAMS,XTI,または TLI で実行するサーバ・アプリケーションをサポートしていません。

/etc/inetd.conf ファイルにリストされている各サーバに対して,inetd デーモンは次の処理を行います。

  1. ソケットを作成して,適切なポート番号をバインドする。

  2. select システム・コールを発行して読み取りを可能にし,そのソケットに対応するサービスに接続を要求するプロセスを待つ。

  3. accept システム・コールを発行してフォークし,dup 呼び出しで新しいソケットをファイル記述子 0 および 1 (stdin と stdout) に複写する。 さらに,他のオープンしているファイル記述子をクローズし,exec 呼び出しで適切なサーバを実行する。

inetd を使用するサーバでは,処理が簡略化されています。 これは,inetd が接続の確立に必要なプロセス間通信処理のほとんどを行っているためです。 inetd によって呼び出されたサーバは,ソケットがファイル記述子 0 と 1 でクライアントに接続されているものと解釈して,ただちに readwritesendrecv などのオペレーションを実行します。

inetd デーモンによって呼び出されたサーバは,<stdio.h> ヘッダ・ファイルにある規約に従い,必要なときに fflush システム・コールを呼び出すという条件付きで,バッファ利用入出力を使用します。 詳細は fflush(3) を参照してください。

getpeername システム・コールは,ソケットのもう一方に接続された対等プロセスのアドレスを返します。 このシステム・コールは,inetd を使用するサーバ・アプリケーションを作成するプログラマにとって役に立ちます。 次のコード例は,inetd を使用してサーバに接続するクライアントのインターネット・アドレス (ドット表記法) のログを取る方法を示しています。

struct sockaddr_storage name;
size_t namelen = sizeof (name);

.
.
.
if (getpeername(0, (struct sockaddr *)&name, &namelen) < 0) { syslog(LOG_ERR, "getpeername: %m"); exit(1); } else syslog(LOG_INFO, "Connection from %s", inet_ntoa(name.sin_addr));
.
.
.

getpeername システム・コールは,inetd を使用するプログラムを作成する場合には特に有効ですが,それ以外の場合にも使用できます。

4.7.9    入出力の多重化

多重化とは,複数のソケット間で入出力要求を伝送したり受信したりするアプリケーションに使用される機能です。 これは,次のように select システム・コールを使用して行うことができます。

#include <sys/time.h>
#include <sys/types.h>
  .
  .
  .
fd_set readmask, writemask, exceptmask;
struct timeval timeout;
  .
  .
  .
if (select(nfds, &readmask, &writemask, &exceptmask, &timeout) < 0)
     perror("select");
 

select システム・コールでは,引数として次の 3 セットのポインタをとります。

  1. 呼び出しているアプリケーションが読み取るデータのためのソケット記述子のセット

  2. データを書き込むソケット記述子

  3. 保留されている例外的な条件

    アプリケーションが読み取り,書き込み,例外など,特定の条件にとらわれない場合,select システム・コールの対応する引数は空ポインタにしなければなりません。

注意

XTI と TLI は STREAMS を使用してインプリメントされているので,STREAMS ファイル記述子についてはすべて,select システム・コールではなく poll システム・コールを使用してください。

各セットは,実際には,整数のビット・マスクの配列を含む構造体です。 配列のサイズは FD_SETSIZE の定義によって設定されます。 配列は,FD_SETSIZE の各ファイル記述子に対して 1 ビットずつの長さが必要です。

mask セットの fd ファイル記述子を追加したり削除したりするために,FD_SET (fd, &mask) および FD_CLR (fd, &mask) マクロが提供されています。 セットは使用する前にゼロに初期化する必要があります。 mask セットをクリアにするために,FD_ZERO (&mask) マクロが提供されています。

select システム・コールの nfds パラメータを使用して,1 セット中でチェックするファイル記述子の範囲,たとえば最大の記述子に 1 をプラスした値などを指定します。

タイムアウト値は,select システム・コールが事前に設定した時間を超えて作動しないように指定するためのものです。 timeout のフィールドがゼロ (0) に設定されている場合,select システム・コールはポールと同様に動作して,ただちに戻ってきます。 最後のパラメータが空ポインタの場合,select システム・コールは,無期限にブロックします。 特に,記述子が選択可能な場合,またはシグナルがシステム・コールに割り込んで呼び出し側に受信された場合に限り,select が戻ります。

通常,select システム・コールは選択した記述子ファイルの数を返します。 タイムアウト時間が満了になったため,select システム・コールが戻る場合には,0 が返されます。 エラーや割り込みのために select システム・コールが終了した場合には,errno にエラー番号が設定され,ファイル記述子のマスクが変更されないまま,--1 が返されます。

select システム・コールが正常に戻った場合,前述の 3 セットは,書き込みや読み取りに使用できるファイル記述子,または保留中の例外条件を持っているファイル記述子を示しています。 select システム・コールのマスクにあるファイル記述子の状態は,FD_ISSET (fd, &mask) マクロを使用して確認することができます。 このマクロは,fdmask セットのメンバである場合は非ゼロを,メンバでない場合は 0 を返します。

accept システム・コールで使用されているソケットに待機している接続があるかどうかを確認するには,FD_ISSET (fd, &mask) マクロを使用して,適当なソケット上で読み取りの準備ができていることを確認した後,select システム・コールを使用します。 FD_ISSET が非ゼロを返して,読み取るデータがあることを示した場合,接続はそのソケット上で待機していることになります。

注意

4.2BSD では,select システム・コールの引数は,fd_set へのポインタではなく,整数へのポインタでした。 この種のシステム・コールは,select システム・コールでチェックされるファイル記述子の数が整数値のビットより少ない場合に限り動作しますが,次のコードで示す方法をお勧めします。

次の例は,1 秒間のタイムアウトを指定して,ソケット s1 および s2 のデータが利用可能になったとき,アプリケーションでデータを読み取る方法を示しています。

#include <sys/time.h>
#include <sys/types.h>

.
.
.
fd_set read_template; struct timeval wait;
.
.
.
for (;;) { wait.tv_sec = 1; /* one second */ wait.tv_usec = 0;   FD_ZERO(&read_template);   FD_SET(s1, &read_template); FD_SET(s2, &read_template);   nb = select(FD_SETSIZE, &read_template, (fd_set *) 0, (fd_set *) 0, &wait); if (nb <= 0) { An error occurred during the select, or the select timed out }   if (FD_ISSET(s1, &read_template)) { Socket #1 is ready to be read from. }   if (FD_ISSET(s2, &read_template)) { Socket #2 is ready to be read from. } }  

select システム・コールは,同期多重化機構を提供します。 4.7.11 項で説明している SIGIO および SIGURG シグナルを使用すると,出力の終了,入力の可能,および例外条件を非同期に通知することができます。

4.7.10    割り込み駆動のソケット I/O

ソケット (通常は,ファイル記述子) に読み取るデータがあるとき,SIGIO シグナルを使用すれば,それをプロセスにシグナル通知することができます。 SIGIO 機能を使用するには,次の 3 つの手順を実行しなければなりません。

  1. signal または sigvec 呼び出しを使用して,プロセスで SIGIO シグナル・ハンドラを起動する。

  2. 保留状態の入力の通知を受信するプロセス ID,またはプロセス・グループ ID を,そのプロセス自身のプロセス ID またはプロセス・グループのプロセス・グループ ID に設定する。

    ソケットの省略時のプロセス・グループはグループ0であることに注意してください。 これらの設定には,fcntl システム・コールを使用します。

  3. 別の fcntl システム・コールで,保留中の入出力要求を非同期に通知できるようにする。

    次のコードは,ソケット s で入出力処理が保留になったときに,特定のプロセスでその情報を受信できるようにする例です。 SIGURG ハンドラを追加すれば,次のようなコードを使用して,SIGURG シグナルを受信することができます。

    #include <fcntl.h>
    
    .
    .
    .
    int io_handler();
    .
    .
    .
    signal(SIGIO, io_handler);   /* Set the process receiving SIGIO/SIGURG signals to us */   if (fcntl(s, F_SETOWN, getpid()) < 0) { perror("fcntl F_SETOWN"); exit(1); }   /* Allow receipt of asynchronous I/O signals */   if (fcntl(s, F_SETFL, FASYNC) < 0) { perror("fcntl F_SETFL, FASYNC"); exit(1); }  

4.7.11    シグナルおよびプロセス・グループ

各ソケットには対応するプロセス番号があり,この番号の値は,ゼロ (0) に初期化されます。 この番号は,4.7.10 項で行ったように,fcntl システム・コールの F_SETOWN パラメータを使用して再定義し,SIGURG および SIGIO シグナルが受信できるようにしなければなりません。 シグナル用にソケットのプロセスID を設定するには,fcntl システム・コールに正の引数を指定しなければなりません。 シグナル用にソケットのプロセス・グループを設定するには,fcntl システム・コールに負の引数を渡さなければなりません。 プロセス番号は,対応するプロセス ID または対応するプロセス・グループを示しています。 両方を同時に指定することはできません。

fcntl システム・コールの F_GETOWN パラメータを使用すると,ソケットの現在プロセス番号を取得することができます。

サーバ・プロセスの作成時には,SIGCHLD シグナルも役立ちます。 このシグナルは,いずれかの子プロセスの状態が変更されると,プロセスに送信されます。 通常,サーバは,明示的に終了を待ったり,終了状態の定期的なポーリングを行わずに,SIGCHLD シグナルを使用して終了した子プロセスを呼び出します。 親サーバ・プロセスが子プロセスを呼び出せない場合には,多数のゾンビ・プロセスが作成されます。 次のコードは,SIGCHLD シグナルの使用方法を示しています。

int reaper();

.
.
.
signal(SIGCHLD, reaper); listen(f, 5); for (;;) { int g; size_t len = sizeof (from);   g = accept(f, (struct sockaddr *)&from, &len,); if (g < 0) { if (errno != EINTR) syslog(LOG_ERR, "rlogind: accept: %m"); continue; }
.
.
.
}
.
.
.
#include <wait.h> reaper() { union wait status;   while (wait3(&status, WNOHANG, 0) > 0) ; }  

4.7.12    擬似端末

ほとんどのプログラムは,標準入出力の端末がなければ正しく機能することができません。 ソケットが端末の意味を提供しないため,ネットワーク上で通信しているプロセスは,擬似端末 (pty) を介して端末の意味を提供する必要があります。 擬似端末とは,マスタとスレーブの 1 対のデバイスであり,アプリケーションとユーザ間の通信において,プロセスをアクティブ・エージェントとして使用することができます。

擬似端末のスレーブ側に書き込まれたデータは,マスタ側から読み込まれたプロセスへの入力として使用され,マスタ側に書き込まれたデータは,スレーブの端末入力として処理されます。 このように,実際の端末でキーボードを操作したりスクリーンを読み取ったりするかのように,擬似端末のマスタ側を操作するプロセスは,スレーブ側で読み取ったり書き込んだ情報を制御します。 擬似端末抽象表現の目的は,ネットワーク接続を通して端末の意味を保持することです。 つまり,スレーブに対して読み込みや書き込みを行うプロセスで,スレーブ側が通常の端末に見えるようにすることです。

たとえば,rlogind リモート・ログイン・サーバは,擬似端末をリモート・ログイン・セッションに使用します。 ネットワーク上でマシンにログインしているユーザには,標準入力,標準出力,および標準エラーとしてシェルにスレーブ擬似端末が提供されます。 すると,サーバ・プロセスは,リモート・シェルから呼び出されたプログラムとユーザのローカル・クライアント・プロセスとの間の通信を処理します。 ユーザが,端末出力をフラッシュするリモート・マシン上に割り込みを生成する文字を送信する場合には,擬似端末はサーバ・プロセスの制御メッセージを生成します。 この場合,サーバは帯域外メッセージをクライアント・プロセスに送信して,実際の端末およびネットワークのバッファに格納された介入データ上でのデータのフラッシュをシグナル通知します。

オペレーティング・システムでは,擬似端末のスレーブ側には /dev/ttyxy 形式の名前があります。 xd 以外の任意の 1 文字で,大文字または小文字も使えます。 y は 16 進数で,0〜9 または a〜f の 1 文字です。 擬似端末のマスタ側には,/dev/ptyxy 形式の名前があります。 xy は,擬似端末のスレーブ側の xy に対応します。

openpty 関数および forkpty 関数が libc.a ライブラリに追加されて,擬似端末の割り当てが容易になりました。 これらの関数は,clone open 呼び出しを使用して,複数の open 呼び出しが実行されないようにします。

forkpty 関数は擬似端末を割り当てます。 また,子プロセスをフォークして,スレーブ擬似端末を子プロセスの制御端末にします。 スレーブのファイル記述子は呼び出しプロセスに渡されないため,forkpty 関数は 5 つではなく 4 つの引数をとります。 代わりに,スレーブのファイル記述子は,新規に作成された子プロセスで,stdinstdout,および stderr として複写されます。 その他の 4 つの引数は,openpty 関数の引数と同じです。

openpty 関数および forkpty 関数は,--1 を返してエラー状態を示します。 openpty 関数は,正常終了するとゼロ (0) を返しますが,forkpty 関数は,子プロセスの pid (プロセス ID) を返します。 関数の構文,パラメータ,エラーについては, openpty(3) を参照してください。

openpty 関数は,次のように動作します。

  1. 正常終了すると,擬似端末のスレーブ側が適切な端末モードに設定される。

    擬似端末のマスタ側とスレーブ側がオープンされるとき,オペレーティング・システムは必要なセキュリティ検査を実行します。

  2. プロセスがフォークする。

    子プロセスは擬似端末のマスタ側をクローズして,適切なプログラムを (exec 呼び出しを使用して) 実行します。

  3. 親プロセスは擬似端末のスレーブ側をクローズして,マスタ側からの読み込みおよび書き込みを開始する。

次の例では,擬似端末を使用しています。 このコード例では,次のように仮定しています。

if (openpty(&mast,&slave,NULL,NULL,NULL) {
   syslog(LOG_ERR, "All network ports in use");
   exit(1);
}
ioctl(slave, TIOCGETA, &term);  /* get default slave termios struct */
term.c_iflag |= ICRNL;
term.c_oflag |= OCRNL;
ioctl(slave, TIOCSETA, &term);  /* set slave characteristics        */
i = fork();
if (i < 0) {
   syslog(LOG_ERR, "fork: %m");
   exit(1);
} else if (i) {      /* Parent */
   close(slave);

.
.
.
} else { /* Child */ (void) close(s); (void) close(master); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); if (slave > 2) (void) close(slave);
.
.
.
}  

ソケットの使用方法についての詳細は,4.3 節を参照してください。