kazmax - Linux で自宅サーバー

accept - システムコールの説明 - Linux コマンド集 一覧表

  1. 名前
  2. 書式
  3. 説明
  4. 注意
  5. 返り値
  6. エラー処理
  7. エラー
  8. 準拠
  9. 注意
  10. 関連項目

名前

accept - ソケットへの接続を受ける

書式

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd , struct sockaddr * addr , socklen_t * addrlen );

説明

accept ()システムコールは、接続指向のソケット型 ( SOCK_STREAM , SOCK_SEQPACKET )で用いられる。 この関数は、保留状態の接続要求が入っているキューから 先頭の接続要求を取り出し、接続済みソケットを新規に生成し、 そのソケットを参照する新しいファイル・ディスクリプタを返す。 新規に生成されたソケットは、接続待ち (listen) 状態ではない。 もともとのソケット sockfd はこの呼び出しによって影響を受けない。

引き数 sockfd は、   socket (2) によって生成され、   bind (2) によってローカルアドレスにバインドされ、   listen (2) を経て接続を待っているソケットである。
addr 引き数は sockaddr 構造体へのポインタである。 この構造体には接続相手のソケットのアドレスが入っている。 addr 引き数で返されるアドレスの正確なフォーマットは、 ソケットのアドレス種別によって変わる ( socket (2)およびそれぞれのプロトコルの man ページを参照)。 addrlen 引き数は入出力両用の引き数で、 最初は addr がポイントする構造体のサイズを格納している。 リターン時には、返されたアドレスの実際の長さ (バイト単位) が入る。 addr が NULL の時は何も代入されない。

キューに保留となっている接続要求がなく、 かつソケットが非停止になっていないときは、 accept ()は接続が発生するまで呼び出し元を停止 (block) する。 ソケットが非停止になっていて、 待ち状態の接続要求がキューに無いときは、 accept ()は EAGAIN エラーで失敗する。

ソケットへの接続到着を知るには、   select (2) または   poll (2) を用いればよい。 新しい接続要求が来るとソケットは読み込み可能になるので、 そうしたら accept ()を呼んでその接続に対するソケットを取得すればよい。 あるいはソケットに設定を行い、何らかのアクションがあったときに SIGIO を配送 (deliver) させるようにすることもできる。詳細は   socket (7) を参照のこと。

明示的な接続確認 (confirmation) を必要とするようなプロトコル (DECNet など) では、 accept ()は単に次の接続要求をキューから取り出すだけであり、 接続確認は行わないことに注意せよ。接続確認は、 新しいファイル・ディスクリプタに対する 通常の読み取り/書き込みによってなされ、接続拒否 (rejection) は新しいソケットをクローズすることによってなされる。 現在のところ、 Linux 上でこれらのセマンティクスを持つのは DECNet だけである。

注意

SIGIO が届けられた後や、   select (2) または   poll (2) が読み込み可能イベントを返した後に、 必ずしも待機中の接続があるとは限らない。 なぜならその接続は、 accept ()が呼ばれる前に、非同期的なネットワークエラーや 他のスレッドから呼ばれた (別の) accept によって 削除されているかもしれないからである。 この場合、その accept ()呼び出しは停止 (block) し、次の接続の到着を待ちつづける。 accept ()に停止を行わせないようにするには、引き数に渡すソケット sockfdO_NONBLOCK フラグをセットしておく必要がある ( socket (7)を見よ)。

返り値

成功した場合、 accept ()は受け付けたソケットのディスクリプタである非負の整数値を返す。 エラーが発生した場合は -1 を返し、 errno を適切に設定する。

エラー処理

Linux の accept ()は、新しいソケットにおける、発生済みのネットワークエラーを accept ()からのエラーコードとして渡す。 この振舞いは BSD ソケットの実装とは異なる。 信頼性の高い動作を行うためには、 アプリケーションはプロトコルで定義されているネットワークエラーの検知を accept ()のあとに行い、それらのエラーを EAGAIN と同じように扱い、再試行 (retry) を行うべきである。 TCP/IP では、以下のエラーが該当する: ENETDOWN , EPROTO , ENOPROTOOPT , EHOSTDOWN , ENONET , EHOSTUNREACH , EOPNOTSUPP , ENETUNREACH

エラー

accept ()は以下のエラーで失敗する:

EAGAIN または EWOULDBLOCK
ソケットが非停止になっていて、 かつ受付け対象の接続が存在しない。
EBADF
ディスクリプタが不正。
ECONNABORTED
接続が中止された。
EINTR
有効な接続が到着する前に捕捉されたシグナルによって システムコールが中断された。
EINVAL
ソケットが接続待ち状態ではない。もしくは、 addrlen が不正である (例えば、負の場合など)。
EMFILE
1プロセスがオープンできるファイル・ディスクリプタ数の上限に達した。
ENFILE
オープンされたファイルの総数がシステム全体の上限に達した。
ENOTSOCK
ディスクリプタはソケットではなくファイルを参照している。
EOPNOTSUPP
参照しているソケットの型が SOCK_STREAM でない。

accept ()は以下のエラーで失敗することがある:

EFAULT
addr 引き数がユーザアドレス空間の書き込み可能領域にない。
ENOBUFS , ENOMEM
メモリが足りない。 多くの場合は、システムメモリが足りないわけではなく、 ソケットバッファの大きさによるメモリ割り当ての制限である。
EPROTO
プロトコル・エラー。

Linux の accept ()は以下のエラーで失敗する:

EPERM
ファイアウォールのルールにより接続が禁止された。

この他に、新しいソケットに対するネットワークエラーが返されることもある。 これらはそれぞれのプロトコルで定義されている。 いろいろな Linux カーネルでは、 以下に示すようなエラーを返すこともある。 ENOSR , ESOCKTNOSUPPORT , EPROTONOSUPPORT , ETIMEDOUT . ERESTARTSYS がトレースの最中に現れることもある。

準拠

SVr4, 4.4BSD ( accept ()は 4.2BSD で初めて実装された)。

Linux では、 accept ()が返す新しいソケットは listen を行っているソケットの ファイル状態フラグ ( O_NONBLOCKO_ASYNC など) を継承「しない」。 この動作は標準的な BSD ソケットの実装とは異なっている。 移植性を考慮したプログラムではファイル状態フラグが継承されるかどうかは 前提にせず、常に accept ()が返したソケットに対して全ての必要なフラグを明示的に設定するように すべきである。

注意

accept ()の第 3 引き数は、もともと `int *' と宣言されていた (libc4 や libc5, 4.x BSD, SunOS 4, SGI など多くのシステムではそうなっている)。 POSIX.1g draft 標準は、 これを `size_t *' に変更しようとし、SunOS 5 ではそう宣言されている。 後に POSIX drafts には `socklen_t *' が含まれるようになり、 Single Unix Specification や glibc2 ではこのように宣言されるようになった。 Linus Torvald の発言を引用する:
「まともなライブラリを作りたければ、 "socklen_t" のサイズは int と同じにしなきゃならない。 さもないと BSD ソケット層を破壊することになっちゃう。 POSIX は最初こいつを size_t にしたんで、 ぼくは彼らに文句をがなりたてた (多分そういう人は他にもいたと思う。多くはなかったようだけど)。 こいつを size_t にするのは完全にいかれてる。 例えば 64 ビットアーキテクチャでは、 size_t が "int" と同じサイズだなんてことはほとんどないからね。 このサイズは "int" と 同じでなきゃ『ダメ』なんだ。 BSD ソケットインターフェースっていうのはそういうものなんだから。 まあともかく POSIX の人たちも、 "socklen_t" を作るという解決策をなんとかひねり出した。 そもそも最初から放っておけば良かったんだが、 いじっちゃった以上、 名前付きの型を持たせなきゃならない、と思ったみたいだね。 なんでかはわかんないけど (きっと最初にやっちまった馬鹿な間違いで顔をつぶしたくなかったから、 こっそり名前を付け替えて自分たちの大失敗をごまかそうとしたんだろう)。」

関連項目