オペレーティング・システムのソケット・プログラミング・インタフェースは,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)
この章では,次の事項について説明します。
ソケット・フレームワークの概要
BSD ソケット・インタフェースについての情報
一般的なソケット・エラー・メッセージの説明
図 4-1は,ソケット・フレームワークを強調表示して,ネットワーク・プログラミング環境における他の部分との関係を示しています。
図 4-1: ソケット・フレームワーク
ソケット通信プロパティを定義する,通信ドメインやソケット・タイプなどの抽象的な表現のセット。
アプリケーション・プログラムがソケット・フレームワークにアクセスするときに使用する,プログラミング・インタフェース。 システム・コールおよびライブラリ・コールのセットからなる。
アプリケーション・プログラムがシステム・コールおよびライブラリ・コールを使用してアクセスする,ネットワーキング・プロトコルを含むカーネル・リソース。
オペレーティング・システムは,プロセス間通信の実現のために,ソケットを使用してインターネット・プロトコル群と UNIX ドメインをインプリメントしています。 また,ソケット・システム・コールを使用してアクセスする,BSD ベースのデバイス・ドライバもインプリメントしています。
この項では,ソケット通信プロパティの基礎となる,抽象表現およびその定義について説明します。
4.1.1.1 ソケット抽象表現
ソケットは,通信の終端として機能します。 単一のソケットは,1 つの終端です。 1 組のソケットで,双方向の通信チャネルを構成して,関連のないプロセス間でローカルに,またはネットワークを通して,データ交換を行うことができるようにします。
アプリケーション・プログラムは,必要に応じて,ソケットの作成をオペレーティング・システムに要求します。 オペレーティング・システムはソケット記述子を返すので,プログラムはこの記述子を使用して,新しく作成されたソケットを参照し,さまざまなオペレーションを行います。
ソケットを参照する記述子を保持しているプロセスがある間だけ存在する。
記述子によって参照され,文字型特殊デバイスと同じ特質を持つ。 適切なシステム・コールを使用することによって,ソケット上で読み取り,書き込み,および選択オペレーションが実行される。
ペアで作成できる。 また,名前を指定し,同じ通信ドメインの他のソケットとのランデブに使用して,それらのソケットからの接続を受け入れたり,メッセージを交換したりすることができる。
ソケットは,通信プロパティに応じてタイプが決まります。
使用できるソケット・タイプについては,4.1.1.3 項を参照してください。
4.1.1.2 通信ドメイン
通信ドメインによって,ハードウェアおよびソフトウェアが異なるシステム間の通信の意味が定義されます。 通信ドメインでは次のことを指定します。
プロトコル・ファミリと呼ばれるプロトコルのセット。
名前を操作および解釈するための規則のセット。
1 つのアドレス・ファミリを構成する,関連のあるソケット・アドレス・フォーマットの集まり。
インターネット通信ドメインのソケット・アドレスには,インターネット・アドレスとポート番号が入っています。 UNIX 通信ドメインのソケット・アドレスには,ローカル・パス名が入っています。
ソケットに関連するデータ構造体についての詳細は,4.2.3.4 項を参照してください。
オペレーティング・システムは,省略時に次のソケット・ドメインをサポートします。 [脚注 16]
UNIX ドメイン
オペレーティング・システムは,AF_UNIX のドメインが指定された場合,同一システム上で実行しているプロセス間にソケット通信を提供します。
UNIX 通信ドメインでは,ソケットは,/dev/printer
のように,UNIX パス名で指定されます。
インターネット・ドメイン
オペレーティング・システムは,AF_INET か AF_INET6 のドメインが指定された場合,ローカルで実行しているプロセスとリモート・ホスト上で実行しているプロセスとの間にソケット通信を提供します。 このドメインには,使用しているシステムで TCP/IP が構成されて動作していることが必要です。
表 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 プロトコル番号に関連付けられ,そのプロトコル用に受信したすべてのトラフィックを受信する。 |
各ソケットには,抽象表現のタイプが関連付けられており,そのソケット・タイプを使用する通信の意味を記述します。
メッセージの信頼性,順序付け,重複の防止などのプロパティは,このソケット・タイプによって決定されます。
ソケット・タイプの基本的なセットは,<sys/socket.h>
ヘッダ・ファイルに定義されています。
注意
通常,ヘッダ・ファイル名は山カッコ (< >) で囲まれています。 ヘッダ・ファイルへの絶対パスは,山カッコ内のヘッダ・ファイル名の前に
/usr/include/
を付けたものです。<sys/socket.h>
の場合,socket.h
は/usr/include/sys
ディレクトリにあります。
UNIX ドメインおよびインターネット・ドメインでは,次のソケット・タイプを使用できます。
固定最大長のコネクションレス型メッセージであるデータグラムを提供する。
各メッセージは,個別にアドレスを指定できます。 このソケット・タイプでは,メッセージの引き渡し順序および信頼性が保証されないので,一般に短いメッセージに使用されます。 データグラム・ソケットの重要な特性は,データのレコード境界が保存されるので,個々のデータグラムを別々に読み取ることにあります。
データグラムは,finger
プログラムのような受信側からの 1 つ以上の応答を必要とする要求に対してよく使用されます。
指定された時間内に受信側が応答しない場合,送信側アプリケーションは要求を繰り返すことができます。
この時間は,通信ドメインによって異なります。
UNIX ドメインでは,SOCK_DGRAM はメッセージ・キューに似ています。 インターネット・ドメインでは,SOCK_DGRAM はユーザ・データグラム・プロトコル (UDP) を使用してインプリメントされます。
帯域外データ用に,接続を通し,伝送機能を伴う連続した双方向バイト・ストリームを提供する。
データの伝送は,高い信頼性で,順番に行われます。
UNIX ドメインでは,SOCK_STREAM は,全二重パイプに似ています。 インターネット・ドメインでは,SOCK_STREAM は,伝送制御プロトコル (TCP) を使用してインプリメントされます。
ネットワーク・プトコロルおよびインタフェースへのアクセスを提供する。
raw ソケットは,特権プロセスだけが利用できます。 raw ソケットを使用すると,アプリケーションは,下位レベルの通信プロトコルに直接アクセスできます。 raw ソケットは,通常のインタフェースを通しては直接アクセスできないプロトコル機能を使用したい上級ユーザ, または既存の下位レベルのプロトコルを使用して,新しいプロトコルを構築したい上級ユーザが使用することを目的としています。 また,SOCK_RAW は,ハードウェア・インタフェースとの通信にも使用できます。
raw ソケットは,通常,データグラム指向です。 ただし,厳密な特性は,プロトコルが提供するインタフェースに依存します。 raw ソケットは,インターネット・ドメインだけで利用できます。
ソケットに名前を付けることによって,システムまたはネットワーク上の関連のないプロセスが,特定のソケットを指定して,そのソケットとデータを交換することができます。 バインドされた名前は,可変長のバイト列であり,サポートしている 1 つまたは複数のプロトコルによって解釈されます。 名前の解釈は,通信ドメインによって異なります。 インターネット・ドメインでは,名前はインターネット・アドレスとポート番号から構成され,ファミリは AF_INET か AF_INET6 のいずれかです。 AF_INET ソケットは IPv4 通信をサポートし,AF_INET6 ソケットは IPv4 通信と IPv6 通信の両方をサポートします。 UNIX ドメインでは,名前はパス名であり,ファミリは AF_UNIX です。
互いに通信しているプロセスは,関連付け (association) によってバインドされます。 インターネット・ドメインでは,関連付けは,プロトコル,ローカル・アドレスと外部アドレス,およびローカル・ポートと外部ポートから構成されます。 インターネット・ドメインのソケットに名前をバインドすると,ローカル・アドレスおよびローカル・ポートが指定されます。
UNIX ドメインでは,関連付けは,ローカル・パス名から構成されます。 UNIX ドメインのソケットに名前をバインドすると,パス名が指定されます。
ほとんどのドメインにおいて,関連付けは一意でなければなりません。
4.2 アプリケーションとソケットとのインタフェース
カーネルにソケットをインプリメントすると,ネットワーキング・サブシステムは,次の 3 つの対話処理層に分かれます。
ソケット層
アプリケーション・プログラムと,伝送制御プロトコル (TCP) またはユーザ・データグラム・プロトコル (UDP) と IP などの下位層との間のインタフェースを提供します。
プロトコル層
トランスポート層プトロコル (TCPおよびUDP) およびネットワーク層プトロコル (IP) から構成されます。
デバイス層
ifnet
層およびデバイス・ドライバから構成されます。
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: ソケット・システム・コール
アプリケーション・プログラムは,分散環境におけるプロセス間通信機能を使用するために,ソケット・ライブラリ・コールを使用して,ネットワーク・アドレスを構築します。
ネットワーク・ライブラリ・サブルーチンは,次の項目をマップします。
ホスト名をネットワーク・アドレスにマップする。
ネットワーク名をネットワーク番号にマップする。
プロトコル名をプロトコル番号にマップする。
サービス名をポート番号にマップする。
名前およびアドレスの操作を簡単にするために,追加のソケット・ライブラリ・コールが用意されています。
ソケット・ライブラリ・コールを使用する場合は,アプリケーション・プログラムに,<netdb.h>
ヘッダ・ファイルをインクルードしなければなりません。
ホスト名
アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,インターネット・ホスト名をアドレスにマップします。
gethostbyname
(AF_INET のみ)
gethostbyaddr
(AF_INET のみ)
getaddrinfo
(AF_INET および AF_INET6)
getnameinfo
(AF_INET および AF_INET6)
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
バージョンの場合には,代替アドレスを返すことができますが,パラメータ値として指定されたもの以外の別名は返しません。
ネットワーク名
アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,ネットワーク名をネットワーク番号に,ネットワーク番号をネットワーク名にマップします。
getnetbyaddr
getnetbyname
getnetent
getnetbyaddr
,getnetbyname
,および
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 */ };
アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,プロトコル名をプロトコル番号にマップします。
getprotobynumber
getprotobyname
getprotoent
getprotobynumber
,getprotobyname
,および
getprotoent
の各サブルーチンは,
/etc/protocols
ファイルから情報を取り出し,次のような
protoent
エントリを返します。
struct protoent { char *p_name; /* official protocol name */ char **p_aliases; /* alias list */ int p_proto; /* protocol number */ };
アプリケーション・プログラムは,次のネットワーク・ライブラリ・ルーチンを使用して,サービス名をポート番号にマップします。
getservbyname
getservbyport
getservent
サービスは,特定のポートに常駐し,特定の通信プロトコルを使用するものとされています。
この考え方は,インターネット・ドメインでは一貫していますが,他のネットワーク・アーキテクチャでは一貫していません。
さらに,サービスは,複数のポートに常駐することがあります。
この場合には,上位レベルのライブラリ・ルーチンを迂回するか,または拡張しなければなりません。
利用できるサービスは,/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 つの関数によって,ネットワークが,ユーザのプログラムから渡されるデータを正しく解釈し,また,ユーザのプログラムが,ネットワークから渡されるデータを解釈することが保証されます。
htonl
htons
ntohl
ntohs
アプリケーション・プログラムは,次の互いに関連するネットワーク・ライブラリ・ルーチンを使用して,インターネット・アドレス文字列とバイナリ・アドレスの操作を行います。
inet_addr
(AF_INET のみ)
inet_lnaof
(AF_INET のみ)
inet_makeaddr
(AF_INET のみ)
inet_netof
(AF_INET のみ)
inet_network
(AF_INET のみ)
inet_ntoa
(AF_INET のみ)
getnameinfo
(AF_INET および AF_INET6)
getaddrinfo
(AF_INET および AF_INET6)
表 4-3
に,ソケット・ライブラリ・コールの一覧と,簡単な説明を示します。
各ライブラリ・コールには,同じ名前の関連するリファレンス・ページがあります。
ソケット・ライブラリ・コールは
libc
の一部であるため,特別なライブラリをリンクする必要はありません。
表 4-3: ソケット・ライブラリ・コール
名前 | 説明 |
endhostent |
一連のホスト・エントリ検索を終了する。 |
endnetent |
一連のネットワーク・エントリ検索を終了する。 |
endprotoent |
一連のプロトコル・エントリ検索を終了する。 |
endservent |
一連のサービス・エントリ検索を終了する。 |
freeaddrinfo |
getaddrinfo
から返された
addrinfo
構造体と記憶域を解放する。 |
getaddrinfo |
ホストの名前とオプションのアドレス・ファミリが指定されると,ネーム・サーバ ( 標準の数値文字列フォーマットのノード・アドレスを, |
gethostbyaddr |
ホストのアドレスが指定されると,ネーム・サーバ (named ) または
/etc/hosts
ファイルのいずれかから,そのホスト・エントリを検索する。 |
gethostbyname |
ホスト名が指定されると,ネーム・サーバ (named ) または
/etc/hosts
ファイルのいずれかから,そのホスト・エントリを検索する。 |
gethostent |
ネーム・サーバ (named ),または
/etc/hosts
ファイル (必要に応じてこのファイルをオープンする) から,次のホスト・エントリを検索する。 |
getnameinfo |
ノードのアドレスを含む
|
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-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 を使用し,ホスト・エントリ,ネットワーク・エントリ,プロトコル・エントリ,サービス・エントリのいずれかを検索する必要がある場合は,アプリケーションにこのファイルをインクルードする必要がある。 |
sockaddr
sockaddr_in
sockaddr_in6
sockaddr_storage
sockaddr_un
msghdr
cmsghdr
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[]; */ };
この節では,ソケットの作成と使用に必要な手順の概略を説明します。 次の手順は,コネクション指向型およびコネクションレス型の通信モードについて説明しています。
socket
および
socketpair
の各システム・コールを使用して,ソケットを作成する方法を説明します。
bind
システム・コールを使用して,名前とアドレスをソケットにバインドする方法を説明します。
クライアントで
connect
システム・コールを使用して,サーバに接続する方法を説明します。
listen
および
accept
の各システム・コールを使用して,サーバをクライアントに接続する方法を説明します。
setsockopt
および
getsockopt
の各システム・コールを使用して,ソケット特性の値の設定および取り出しを行う方法を説明します。
read
と
write
のシステム・コール,および
send
と
recv
関連のシステム・コールを使用して,データの伝送を行う方法を説明します。
shutdown
システム・コールを使用して,ソケットをシャットダウンする方法を説明します。
close
システム・コールを使用して,ソケットをクローズする方法を説明します。
ソケットを使用する最初の手順は,ソケットの作成です。
ソケットは,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"); }
.
.
.
}
ソケットは,ブロッキングまたは非ブロッキングのいずれかの 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 エラー・コードを返します。
accept
connect
send
sendto
sendmsg
recv
recvfrom
recvmsg
read
write
これらのシステム・コールを非ブロッキングのソケットで使用するプロセスは,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.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 )
コネクション指向型のサーバ・プロセスは,通常,周知のアドレスでサービス要求をリッスンします。 つまり,サーバ・プロセスは,クライアントをサーバ・アドレスに接続することによって接続が要求されるまで,休止状態のままです。 接続が要求されると,サーバ・プロセスはウェイクアップし,クライアントが要求するアクションを実行してクライアントに対してサービスを行います。
コネクション指向型サーバは,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 データの転送
ソケット層で行われる作業の大半は,データの送受信です。 ソケット層自体は,ソケットを通して送受信されるデータに対して,いかなる構造体の使用も義務付けていません。 すべてのデータの解釈または構築は,通信ドメインのインプリメンテーションとは論理的に分離されています。
アプリケーションがデータの送受信に使用するシステム・コールは,次のとおりです。
read
write
send
sendto
recv
recvfrom
sendmsg
recvmsg
read
システム・コールによって,送信側のアドレスを受信せずにプロセスがソケットでデータを受信できます。
関数の構文,パラメータ,エラーについては,
read
(2)4.3.6.2 write システム・コールの使用
write
システム・コールは,接続状態のソケットで使用します。
write
システム・コールを使用して転送されるデータのデスティネーションは,接続によって暗黙的に指定されます。
関数の構文,パラメータ,エラーについては,
write
(2)4.3.6.3 send, sendto, recv および recvfrom システム・コールの使用
send
,sendto
,recv
,および
recvfrom
の各システム・コールは,read
および
write
のシステム・コールとよく似ています。
いずれも,最初の 3 つのパラメータは同じですが,send
,sendto
,recv
,および
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
システム・コールは,接続されたソケット,または接続されていないソケットで使用されます。
このシステム・コールは,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); } . . .
アプリケーション・プログラムは,すべての保留中のデータが必要でない場合には,クローズする前に,ソケットに対して
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 ソケットの使用
アプリケーションは
gethostbyname
を呼び出し,ホスト名
host1
を渡す。
検索によって hosts データベースから
host1
を発見し,gethostbyname
は IPv4 アドレス
1.2.3.4
を,hostent
型の構造体に返す。
アプリケーションは
socket
を呼び出し,AF_INET
ソケットを作成する。
この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。
socket
呼び出しが成功すると,アプリケーションは
sockaddr_in
構造体を設定して
connect
を呼び出し,host1
とのコネクションを確立する。
connect
呼び出しが成功すると,アプリケーションは
send
を呼び出して,情報をアドレス
1.2.3.4
に送信する。
ソケット層は,情報とアドレスを UDP モジュールに渡す。
UDP モジュールはアドレス
1.2.3.4
をパケット・ヘッダにいれ,情報を IPv4 に渡し伝送する。
recv
を呼び出し,サーバ・システムからの応答を待つ。
応答を受信した後,gethostbyaddr
を呼び出し,sockaddr_in
構造体でサーバのアドレスを渡す。
検索によりそのアドレスがホスト・データベース内で見つかると,gethostbyaddr
は,hostent
型の構造体でホスト名を返す。
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 ソケットの使用
アプリケーションは
getaddrinfo
を呼び出し,ホスト名 (host1
),AF_INET6
アドレス・ファミリ・ヒント,AI_ADDRCONFIG
フラグ・ヒント,AI_V4MAPPED
フラグ・ヒントを渡す。
このフラグ・ヒントは,host1
の IPv6 アドレスが見つかった場合は,それを返すように関数に指示する。
ヒントのフィールドと値についての詳細は,
getaddrinfo
(3)
検索によって hosts データベースで
host1
の IPv6 アドレスを発見し,getaddrinfo
は IPv6 アドレス
3ffe:1200::a00:2bff:fe2d:02b2
を,1 つ以上の
addrinfo
型の構造体に返す。
アプリケーションは
socket
を呼び出し,addrinfo
構造体内のアドレス・ファミリおよびソケット・タイプを使用して,AF_INET6
ソケットを作成する。
この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。
socket
呼び出しが成功すると,アプリケーションは
addrinfo
構造体内のホスト・アドレスと長さを使用して
connect
を呼び出し,host1
とのコネクションを確立する。
connect
呼び出しが成功すると,アプリケーションは情報をアドレス
3ffe:1200::a00:2bff:fe2d:02b2
に送信する。
注意
addrinfo
構造体の情報を使用した後,アプリケーションはfreeaddrinfo
を呼び出して,この構造体に使用されていたシステム・リソースを解放します。
ソケット層は,情報とアドレスを UDP モジュールに渡す。
UDP モジュールは IPv6 アドレスを識別し,アドレス
3ffe:1200::a00:2bff:fe2d:02b2
をパケット・ヘッダにいれ,情報を IPv6 モジュールに渡し伝送する。
recv
を呼び出し,サーバ・システムからの応答を待つ。
応答を受信した後,getpeername
を呼び出し,接続されたソケットのアドレスを調べる。
このアドレスは,sockaddr_in6
型の構造体で返される。
アプリケーションがプロトコルに依存しないようにしたい場合は,sockaddr_in6
構造体の代わりに
sockaddr_storage
構造体を使用することもできる。
NI_NAMEREQD
フラグを指定して
getnameinfo
を呼び出し,サーバ名を取得する。
NI_NUMERICHOST
フラグを指定して
getnameinfo
を呼び出し,サーバ・アドレスをテキスト文字列に変換する。
C.2.1 項 には,これらの手順を示す,クライアント・プログラム・コードの例があります。
AF_INET6 ソケットは,IPv4 通信に使用することもできます。
図 4-4
では,AF_INET6 ソケットを使用して IPv4 パケットを送信するクライアント・アプリケーションのイベントのシーケンスを示しています。
図 4-4: IPv4 通信での AF_INET6 ソケットの使用 (送信)
アプリケーションは
getaddrinfo
を呼び出し,ホスト名 (host1
),AF_INET6
アドレス・ファミリ・ヒント,AI_ADDRCONFIG
フラグ・ヒント,AI_V4MAPPED
フラグ・ヒントを渡す。
このフラグ・ヒントは,host1
の IPv4 アドレスが見つかった場合は,それを IPv4 にマップされた IPv6 アドレスとして返すように関数に指示する。
ヒントのフィールドと値についての詳細は,
getaddrinfo
(3)
検索によって hosts データベースで
host1
の IPv4 アドレス
1.2.3.4
を発見し,getaddrinfo
は IPv4 にマップされた IPv6 アドレス
::ffff:1.2.3.4
を,1 つ以上の
addrinfo
型の構造体に返す。
アプリケーションは
socket
を呼び出し,addrinfo
構造体内のアドレス・ファミリおよびソケット・タイプを使用して,AF_INET6
ソケットを作成する。
この例ではこのソケットはデータグラム・ソケット (UDP) だが,ストリーム・ソケット (TCP) とすることもできる。
socket
呼び出しが成功すると,アプリケーションは
addrinfo
構造体内のホスト・アドレスと長さを使用して
connect
を呼び出し,host1
とのコネクションを確立する。
connect
呼び出しが成功すると,アプリケーションは情報をアドレス
::ffff:1.2.3.4
に送信する。
注意
addrinfo
構造体の情報を使用した後,アプリケーションはfreeaddrinfo
を呼び出して,この構造体に使用されていたシステム・リソースを解放します。
ソケット層は,情報とアドレスを UDP モジュールに渡す。
UDP モジュールは IPv4 にマップされた IPv6 アドレスを識別し,アドレス
1.2.3.4
をパケット・ヘッダにいれ,情報を IPv4 モジュールに渡し伝送する。
recv
を呼び出し,サーバ・システムからの応答を待つ。
応答を受信した後,getpeername
を呼び出し,接続されたソケットのアドレスを調べる。
このアドレスは,sockaddr_in6
型の構造体で返される。
アプリケーションがプロトコルに依存しないようにしたい場合は,sockaddr_in6
構造体の代わりに
sockaddr_storage
構造体を使用することもできる。
NI_NAMEREQD
フラグを指定して
getnameinfo
を呼び出し,サーバ名を取得する。
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 ソケットの使用 (受信)
アプリケーションは次の処理を行う。
socket
を呼び出して,AF_INET6
ソケットを作成する。
sockaddr_in6
構造体を初期化し,ファミリ,アドレス,ポートを設定する。
bind
を呼び出して,ソケットにアドレスを割り当てる。
listen
を呼び出して,ソケットをコネクション受け付け状態にする。
IPv4 パケットが到着し,IPv4 モジュールを通過する。
TCP レイヤでは,パケット・ヘッダを削除し,その情報と IPv4 にマップされた IPv6 アドレス (::ffff:1.2.3.4
) をソケット・レイヤへ渡す。
アプリケーションは
accept
を呼び出し,ソケットから情報を取り出す。
ソケットからの情報は,sockaddr_storage
構造体でアプリケーションに渡される。
これにより,アプリケーションがプロトコルに依存しなくなる。
アプリケーションは
getnameinfo
を呼び出し,アドレス
::ffff:1.2.3.4
と
NI_NAMEREQD
フラグを渡す。
このフラグは,アドレスに対応するホスト名を返すように関数に指示する。
フラグ・ビットとその意味についての詳細は,
getnameinfo
(3)
検索の結果,アドレス
1.2.3.4
のホスト名が
hosts
データベースに見つかり,getnameinfo
はホスト名を返す。
AF_INET6 ソケットを通信に使用するアプリケーションは,場合によっては,構造体に返されるアドレスのタイプを調べる必要があります。 このような場合のために,API にはアドレスをテストするマクロが定義されています。 表 4-5 に,現在定義されているアドレス・テスト・マクロと,有効なテストで返される値をリストします。 これらのマクロを使用するには,アプリケーション内で次のファイルをインクルードします。
#include <netinet/in.h>
マクロ | 返却値 |
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。 |
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 インタフェースから次の変更が加えられています。
可変長 (長い) のネットワーク・アドレスをサポートするための
sockaddr
構造体
データとともにプロトコル情報および状態の受信を可能にする
msghdr
構造体
以降の項で,これらの機能について説明します。
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.3BSD バージョンの
msghdr
構造体 (cc
コマンドを使用するときの省略時の設定) は,sendmsg
および
recvmsg
の各システム・コールのオプション機能を使用するときに必要なパラメータを提供します。
/* 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.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 |
次の構造体は,構造体名とフィールド名が変更されています。
in_addr
sockaddr_in
sockaddr
hostent
以降の項で,これらの変更について説明します。
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]
|
必要に応じて,アプリケーションを次のように変更します。
構造体名
in_addr
を
in6_addr
に変更する。
[例に戻る]
データ型を
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]
|
必要に応じて,アプリケーションを次のように変更します。
構造体名
sockaddr_in
を
sockaddr_in6
に変更する。
[例に戻る]
データ型を
unsigned char
から
uint8_t
に,フィールド名を
sin_len
から
sin6_len
に変更する。
[例に戻る]
フィールド名を
sin_family
から
sin6_family
に変更する。
[例に戻る]
フィールド名を
sin_port
から
sin6_port
に変更する。
[例に戻る]
フィールド名を
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]
|
必要に応じて,アプリケーションを次のように変更します。
構造体名
sockaddr_in
を
sockaddr_in6
に変更する。
[例に戻る]
フィールド名
sin_family
を
sin6_family
に変更する。
[例に戻る]
フィールド名
sin_port
を
sin6_port
に変更する。
[例に戻る]
フィールド名
sin_addr
を
sin6_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]
|
struct sockaddr_in
にすべきところは,構造体名
sockaddr
を
sockaddr_in6
に変更する。
たとえば,sizeof(struct sockaddr)
。
[例に戻る]
アプリケーションが IPv4 と IPv6 の両方のノードから送られてくるアドレスを操作する場合は,必要に応じて次の変更をアプリケーションに行います。
AF_INET の構造体 | AF_INET6 の構造体 |
struct sockaddr [1]
|
struct sockaddr_storage [1]
|
struct sockaddr_in
にすべきところは,構造体名
sockaddr
を
sockaddr_storage
に変更する。
たとえば,sizeof(struct sockaddr)
。
[例に戻る]
注意
sockaddr_in6
構造体とsockaddr_storage
構造体は,どちらもサイズがsockaddr
構造体よりも大きくなっています。
hostent 構造体
hostent
構造体を使用するアプリケーションは,次の例に示すように,addrinfo
構造体を使用するように必要に応じて変更しなければなりません。
AF_INET の構造体 | AF_INET6 の構造体 |
struct hostent [1]
|
struct addrinfo [1]
|
必要に応じて,アプリケーションを次のように変更します。
構造体名
hostent
を
addrinfo
に変更する。
[例に戻る]
次のライブラリ・ルーチンを使用しているアプリケーションは,必要に応じて変更しなければなりません。
gethostbyaddr
gethostbyname
inet_ntoa
inet_addr
以降の項で,これらの変更について説明します。
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]
|
必要に応じて,アプリケーションを次のように変更します。
関数名を
gethostbyaddr
から
getnameinfo
に変更し,ソケット・アドレス構造体へのポインタ,返されるノード名用の文字列,返されるノード名の長さを示す整数値,返されるサービス名用の文字列,返されるサービス名の長さを示す整数値,実行されるアドレス処理のタイプを示す整数値を指定する。
フラグ・ビットとその意味についての詳細は,
getnameinfo
(3)
gethostbyname
関数を呼び出すアプリケーションは,次の例に示すように,getaddrinfo
関数を呼び出すように変更しなければなりません。
AF_INET の呼び出し |
gethostbyname(name) [1]
|
AF_INET6 の呼び出し |
err=getaddrinfo(node_name, service_name, &hints, &result); [1]
|
必要に応じて,アプリケーションを次のように変更します。
関数名を
gethostbyname
から
getaddrinfo
に変更し,ノード名の文字列,使用するサービス名の文字列,処理オプションが格納されている
hints
構造体へのポインタ,addrinfo
構造体 (アドレス情報が返される構造体) へのポインタを指定する。
ヒントのフィールドと値についての詳細は,
getaddrinfo
(3)
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]
|
必要に応じて,アプリケーションを次のように変更します。
関数名を
gethostbyaddr
から
getnameinfo
に変更し,ソケット・アドレス構造体へのポインタ,返されるノード名用の文字列,返されるノード名の長さを示す整数値,返されるサービス名用の文字列,返されるサービス名の長さを示す整数値,NI_NUMERICHOST フラグを指定する。
フラグ・ビットとその意味についての詳細は,
getnameinfo
(3)
inet_addr
関数を呼び出すアプリケーションは,次の例に示すように,getaddrinfo
関数を呼び出すように変更しなければなりません。
AF_INET の呼び出し |
result=inet_addr(&string); [1]
|
AF_INET6 の呼び出し |
err=getaddrinfo(node_name, service_name, &hints, &result); [1]
|
必要に応じて,アプリケーションを次のように変更します。
関数名を
inet_addr
から
getaddrinfo
に変更し,ノード名の文字列,使用するサービス名の文字列,AI_NUMERICHOST オプションが格納されている
hints
構造体へのポインタ,addrinfo
構造体 (アドレス情報が返される構造体) へのポインタを指定する。
ヒントのフィールドと値についての詳細は,
getaddrinfo
(3)
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]
|
等値比較式を,memcmp
(メモリの比較) 関数を使用したものに変更する。
[例に戻る]
AF_INET のコード | AF_INET6 のコード |
(addr1->s_addr == addr2->s_addr) [1]
|
IN6_ARE_ADDR_EQUAL(addr1, addr2) [1]
|
アプリケーションが IP アドレスをワイルドカード・アドレスと比較している場合,4.7.1.2 項
で行った
in6_addr
構造体への変更により,int
の数値の比較が構造体の比較に変わります。
これによってコードが誤った状態になり,コンパイル時にエラーになります。
AF_INET のコード | AF_INET6 のコード |
(addr->s_addr == INADDR_ANY) [1]
|
IN6_IS_ADDR_UNSPECIFIED(addr) [1]
|
AF_INET のコード |
(addr->s_addr == INADDR_ANY) [1]
|
AF_INET6 のコード |
(memcmp(addr, in6addr_any, sizeof(struct in6_addr)) == 0) [1]
|
等値比較式を,memcmp
(メモリの比較) 関数を使用したものに変更する。
[例に戻る]
アプリケーションが
int
データ型を用いて IP アドレスを保持している場合,4.7.1.2 項
で行ったin6_addr
構造体への変更によって,代入が変わります。
これによってコードが誤った状態になり,コンパイル時にエラーになります。
AF_INET のコード | AF_INET6 のコード |
struct in_addr foo; int bar; [1]
|
struct in6_addr foo; struct in6_addr bar; [1]
|
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]
|
関数を再構築して,呼び出し時に構造体のアドレスを渡せるようにする。 さらに,関数を変更して 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 ソケットを使うと,アプリケーションは次の情報にアクセスできます。
ICMPv6 メッセージ
IPv6 ヘッダ
ルーティング・ヘッダ
IPv6 オプション・ヘッダ (ホップ・バイ・ホップ・オプション・ヘッダとデスティネーション・オプション・ヘッダ)
この項では,これらの情報にアクセスする方法について説明します。
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 アドレス
インタフェース・インデックス
ホップ限界値
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_reverse
,inet6_rth_segments
,inet6_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)
アプリケーションがルーティング・ヘッダを指定するときには,connect
,sendto
,または
sendmsg
の呼び出しで指定されたデスティネーション・アドレスが,データグラムの最終デスティネーションになります。
このため,ルーティング・ヘッダには,すべての中間ノードのアドレスが入っています。
RFC 2460 で規定されている拡張ヘッダの順序の都合上,アプリケーションは送信ルーティング・ヘッダを 1 つだけ指定できます。
4.7.2.4 IPv6 オプション・ヘッダへのアクセス
Advanced Sockets API を使うと,アプリケーションは次の IPv6 オプション・ヘッダにアクセスできます。
ホップ・バイ・ホップ
1 つのホップ・バイ・ホップ・オプション・ヘッダに,複数のホップ・バイ・ホップ・オプションを入れることができます。 各オプションは,タイプ,長さ,値 (TLV) がコード化されています。 アプリケーションはスティッキ・オプションまたは補助データを使って,この情報をカーネルとやりとりします。
デスティネーション
1 個以上のデスティネーション・オプション・ヘッダに,複数のデスティネーション・オプションを入れることができます。 ルーティング・ヘッダより前にあるデスティネーション・オプション・ヘッダは,ルーティング・ヘッダ内に指定されている 1 番目以降のデスティネーションによって処理されます。 ルーティング・ヘッダより後にあるデスティネーション・オプションは,最後のデスティネーションによってのみ処理されます。 各オプションは,タイプ,長さ,値 (TLV) がコード化されています。 アプリケーションはスティッキ・オプションまたは補助データを使って,この情報をカーネルとやりとりします。
ヘッダの境界合わせの要件や拡張ヘッダの順序についての詳細は,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_next
,inet6_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)cmsg_level
メンバに IPPROTO_IPV6 を設定し,cmsg_type
メンバに IPV6_DSTOPTS を設定します。
アプリケーションは,inet6_opt_next
,inet6_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_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)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);
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); }
.
.
.
}
帯域外データは,接続されたストリーム・ソケットの各ペアに関連付けられた,論理的に独立している伝送チャネルです。
帯域外データは,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
システム・コールを使用して,アプリケーションで設定できます。
Time-to-live (TTL) フィールド (IP_MULTICAST_TTL
)
マルチキャスト・インタフェース (IP_MULTICAST_IF
)
ローカル引き渡しのループバックの使用不能化 (IP_MULTICAST_LOOP
)
注意
setsockopt
システム・コールの構文および引数については,4.3.5 項およびに説明があります。 この項および4.7.6.2 項の例は,IPv4 マルチキャスト・データグラムだけに適用される setsockopt
(2)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 変数の構造体を次に示します。
struct
ip_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
システム・コールを使用して,アプリケーションで設定できます。
ホップの限界値 (IPV6_MULTICAST_HOPS
)
マルチキャスト・インタフェース (IPV6_MULTICAST_IF
)
ローカル引き渡しのループバックの使用不能化 (IPV6_MULTICAST_LOOP
)
注意
setsockopt
システム・コールの構文および引数については,4.3.5 項およびに説明があります。 この項および4.7.6.4 項の例は,IPv6 マルチキャスト・データグラムだけに適用される setsockopt
(2)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 変数の構造体を次に示します。
struct
ipv6_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 のようなコネクション指向型トランスポートではサポートされていません。
ブロードキャスト・メッセージを送信するには,次の手順に従います。
データグラム・ソケットを作成する。
たとえば,次のように作成します。
s = socket(AF_INET, SOCK_DGRAM, 0);
ソケットをブロードキャスト用に指定する。
たとえば,次のようになります。
int on = 1; if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) perror("setsockopt");
少なくとも 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
デーモンは次の処理を行います。
ソケットを作成して,適切なポート番号をバインドする。
select
システム・コールを発行して読み取りを可能にし,そのソケットに対応するサービスに接続を要求するプロセスを待つ。
accept
システム・コールを発行してフォークし,dup
呼び出しで新しいソケットをファイル記述子 0 および 1 (stdin と stdout) に複写する。
さらに,他のオープンしているファイル記述子をクローズし,exec
呼び出しで適切なサーバを実行する。
inetd
を使用するサーバでは,処理が簡略化されています。
これは,inetd
が接続の確立に必要なプロセス間通信処理のほとんどを行っているためです。
inetd
によって呼び出されたサーバは,ソケットがファイル記述子 0 と 1 でクライアントに接続されているものと解釈して,ただちに
read
,write
,send
,recv
などのオペレーションを実行します。
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 セットのポインタをとります。
呼び出しているアプリケーションが読み取るデータのためのソケット記述子のセット
データを書き込むソケット記述子
保留されている例外的な条件
アプリケーションが読み取り,書き込み,例外など,特定の条件にとらわれない場合,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) マクロを使用して確認することができます。
このマクロは,fd
が
mask
セットのメンバである場合は非ゼロを,メンバでない場合は 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 つの手順を実行しなければなりません。
signal
または
sigvec
呼び出しを使用して,プロセスで SIGIO シグナル・ハンドラを起動する。
保留状態の入力の通知を受信するプロセス ID,またはプロセス・グループ ID を,そのプロセス自身のプロセス ID またはプロセス・グループのプロセス・グループ ID に設定する。
ソケットの省略時のプロセス・グループはグループ0であることに注意してください。
これらの設定には,fcntl
システム・コールを使用します。
別の
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); }
各ソケットには対応するプロセス番号があり,この番号の値は,ゼロ (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) ; }
ほとんどのプログラムは,標準入出力の端末がなければ正しく機能することができません。
ソケットが端末の意味を提供しないため,ネットワーク上で通信しているプロセスは,擬似端末 (pty
) を介して端末の意味を提供する必要があります。
擬似端末とは,マスタとスレーブの 1 対のデバイスであり,アプリケーションとユーザ間の通信において,プロセスをアクティブ・エージェントとして使用することができます。
擬似端末のスレーブ側に書き込まれたデータは,マスタ側から読み込まれたプロセスへの入力として使用され,マスタ側に書き込まれたデータは,スレーブの端末入力として処理されます。 このように,実際の端末でキーボードを操作したりスクリーンを読み取ったりするかのように,擬似端末のマスタ側を操作するプロセスは,スレーブ側で読み取ったり書き込んだ情報を制御します。 擬似端末抽象表現の目的は,ネットワーク接続を通して端末の意味を保持することです。 つまり,スレーブに対して読み込みや書き込みを行うプロセスで,スレーブ側が通常の端末に見えるようにすることです。
たとえば,rlogind
リモート・ログイン・サーバは,擬似端末をリモート・ログイン・セッションに使用します。
ネットワーク上でマシンにログインしているユーザには,標準入力,標準出力,および標準エラーとしてシェルにスレーブ擬似端末が提供されます。
すると,サーバ・プロセスは,リモート・シェルから呼び出されたプログラムとユーザのローカル・クライアント・プロセスとの間の通信を処理します。
ユーザが,端末出力をフラッシュするリモート・マシン上に割り込みを生成する文字を送信する場合には,擬似端末はサーバ・プロセスの制御メッセージを生成します。
この場合,サーバは帯域外メッセージをクライアント・プロセスに送信して,実際の端末およびネットワークのバッファに格納された介入データ上でのデータのフラッシュをシグナル通知します。
オペレーティング・システムでは,擬似端末のスレーブ側には
/dev/ttyxy
形式の名前があります。
x
は
d
以外の任意の 1 文字で,大文字または小文字も使えます。
y
は 16 進数で,0〜9 または a〜f の 1 文字です。
擬似端末のマスタ側には,/dev/ptyxy
形式の名前があります。
x
と
y
は,擬似端末のスレーブ側の
x
と
y
に対応します。
openpty
関数および
forkpty
関数が
libc.a
ライブラリに追加されて,擬似端末の割り当てが容易になりました。
これらの関数は,clone open
呼び出しを使用して,複数の
open
呼び出しが実行されないようにします。
forkpty
関数は擬似端末を割り当てます。
また,子プロセスをフォークして,スレーブ擬似端末を子プロセスの制御端末にします。
スレーブのファイル記述子は呼び出しプロセスに渡されないため,forkpty
関数は 5 つではなく 4 つの引数をとります。
代わりに,スレーブのファイル記述子は,新規に作成された子プロセスで,stdin
,stdout
,および
stderr
として複写されます。
その他の 4 つの引数は,openpty
関数の引数と同じです。
openpty
関数および
forkpty
関数は,--1 を返してエラー状態を示します。
openpty
関数は,正常終了するとゼロ (0) を返しますが,forkpty
関数は,子プロセスの pid (プロセス ID) を返します。
関数の構文,パラメータ,エラーについては,
openpty
(3)
openpty
関数は,次のように動作します。
正常終了すると,擬似端末のスレーブ側が適切な端末モードに設定される。
擬似端末のマスタ側とスレーブ側がオープンされるとき,オペレーティング・システムは必要なセキュリティ検査を実行します。
プロセスがフォークする。
子プロセスは擬似端末のマスタ側をクローズして,適切なプログラムを (exec
呼び出しを使用して) 実行します。
親プロセスは擬似端末のスレーブ側をクローズして,マスタ側からの読み込みおよび書き込みを開始する。
次の例では,擬似端末を使用しています。 このコード例では,次のように仮定しています。
ソケットの接続が存在する。
ソケットは,特定のサービスを要求する対等プロセスに接続されている。
プロセスは,以前の制御端末からの関連をすべて解除されている。
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 節を参照してください。