accept, accept4 -
ソケットへの接続を受ける
#include <sys/types.h> /* 「注意」参照 */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define _GNU_SOURCE /* feature_test_macros(7) 参照 */
#include <sys/socket.h>
int accept4(int sockfd, struct sockaddr *addr,
socklen_t *addrlen, int flags);
accept()
システムコールは、接続指向のソケット型
(
SOCK_STREAM,
SOCK_SEQPACKET)
で用いられる。
この関数は、接続待ちソケット
socket
宛ての保留状態の接続要求が入っているキューから
先頭の接続要求を取り出し、接続済みソケットを新規に生成し、
そのソケットを参照する新しいファイルディスクリプターを返す。
新規に生成されたソケットは、接続待ち
(listen) 状態ではない。
もともとのソケット
sockfd
はこの呼び出しによって影響を受けない。
引数
sockfd は、
socket(2)
によって生成され、
bind(2)
によってローカルアドレスにバインドされ、
listen(2)
を経て接続を待っているソケットである。
addr 引数は
sockaddr
構造体へのポインターである。
この構造体には接続相手のソケットのアドレスが入っている。
addr
引数で返されるアドレスの正確なフォーマットは、
ソケットのアドレス種別によって変わる
(
socket(2)
およびそれぞれのプロトコルの
man ページを参照)。
addr
が NULL の場合、
addr
には何も入らない。この場合、
addrlen
は使用されず、この引数は
NULL
にしておくべきである。
addrlen
引数は入出力両用の引数である。呼び出し時には、呼び出し元が
addr
が指す構造体のサイズ
(バイト単位)
で初期化しておかなければならない。
返ってくる時には、接続相手のアドレスの実際の大きさが格納される。
渡されたバッファーが小さ過ぎた場合は、返されるアドレスの末尾が切り詰められる。
この場合には、
addrlen
には、呼び出し時に指定された値よりも大きな値が格納される。
キューに保留となっている接続要求がなく、
かつソケットが非停止になっていないときは、
accept()
は接続が発生するまで呼び出し元を停止
(block) する。
ソケットが非停止になっていて、
待ち状態の接続要求がキューに無いときは、
accept() はエラー
EAGAIN か
EWOULDBLOCK で失敗する。
In order to be notified of incoming connections on a socket, you can use
select(2),
poll(2), or
epoll(7). A readable event will be
delivered when a new connection is attempted and you may then call
accept() to get a socket for that connection. Alternatively, you can
set the socket to deliver
SIGIO when activity occurs on a socket; see
socket(7) for details.
flags が 0 の場合、
accept4()
は
accept()
と同じである。
flags
に以下の値をビット毎の論理和
(OR) で指定することで、
異なる動作をさせることができる。
- SOCK_NONBLOCK
- Set the O_NONBLOCK file status flag on the open file
description (see open(2)) referred to by the new file descriptor.
Using this flag saves extra calls to fcntl(2) to achieve the same
result.
- SOCK_CLOEXEC
- 新しいファイルディスクリプターに対して
close-on-exec ( FD_CLOEXEC)
フラグをセットする。
このフラグが役に立つ理由については、
open(2) の O_CLOEXEC
フラグの説明を参照のこと。
On success, these system calls return a file descriptor for the accepted socket
(a nonnegative integer). On error, -1 is returned,
errno is set
appropriately, and
addrlen is left unchanged.
Linux の
accept() (と
accept4())
は、新しいソケットにおける、発生済みのネットワークエラーを
accept()
からのエラーコードとして渡す。
この振舞いは BSD
ソケットの実装とは異なる。
信頼性の高い動作を行うためには、
アプリケーションはプロトコルで定義されているネットワークエラーの検知を
accept()
のあとに行い、それらのエラーを
EAGAIN
と同じように扱い、再試行
(retry)
を行うべきである。
TCP/IP
では、以下のエラーが該当する:
ENETDOWN,
EPROTO,
ENOPROTOOPT,
EHOSTDOWN,
ENONET,
EHOSTUNREACH,
EOPNOTSUPP,
ENETUNREACH
-
EAGAIN または
EWOULDBLOCK
- ソケットが非停止になっていて、
かつ受付け対象の接続が存在しない。
POSIX.1-2001 と POSIX.1-2008
は、この場合にどちらのエラーを返すことも認めており、
これら 2
つの定数が同じ値を持つことも求めていない。
したがって、移植性が必要なアプリケーションでは、両方の可能性を
確認すべきである。
- EBADF
-
sockfd
がオープンされたファイルディスクリプターでない。
- ECONNABORTED
- 接続が中止された。
- EFAULT
-
addr
引数がユーザーアドレス空間の書き込み可能領域にない。
- EINTR
- 有効な接続が到着する前に捕捉されたシグナルによって
システムコールが中断された。
signal(7) 参照。
- EINVAL
- ソケットが接続待ち状態ではない。もしくは、
addrlen が不正である
(例えば、負の場合など)。
- EINVAL
- (accept4()) flags
に不正な値が指定されている。
- EMFILE
- 1プロセスがオープンできるファイルディスクリプター数の上限に達した。
- ENFILE
- オープンされたファイルの総数がシステム全体の上限に達していた。
-
ENOBUFS, ENOMEM
- メモリーが足りない。
多くの場合は、システムメモリーが足りないわけではなく、
ソケットバッファーの大きさによるメモリー割り当ての制限である。
- ENOTSOCK
- ファイルディスクリプター
sockfd
がソケットを参照していない。
- EOPNOTSUPP
- 参照しているソケットの型が
SOCK_STREAM でない。
- EPROTO
- プロトコルエラー。
上記に加えて、Linux の
accept()
は以下のエラーで失敗する:
- EPERM
- ファイアウォールのルールにより接続が禁止された。
この他に、新しいソケットに対するネットワークエラーが返されることもある。
これらはそれぞれのプロトコルで定義されている。
いろいろな Linux
カーネルでは、
以下に示すようなエラーを返すこともある。
ENOSR,
ESOCKTNOSUPPORT,
EPROTONOSUPPORT,
ETIMEDOUT.
ERESTARTSYS
がトレースの最中に現れることもある。
accept4()
システムコールは Linux 2.6.28
以降で利用可能である。
glibc
でのサポートはバージョン
2.10
以降で利用可能である。
accept(): POSIX.1-2001, POSIX.1-2008, SVr4, 4.4BSD, (
accept()
は 4.2BSD
で初めて実装された).
accept4() は非標準の Linux
による拡張である。
Linux では、
accept()
が返す新しいソケットは
listen
を行っているソケットの
ファイル状態フラグ (
O_NONBLOCK や
O_ASYNC など)
を継承「しない」。
この動作は標準的な BSD
ソケットの実装とは異なっている。
移植性を考慮したプログラムではファイル状態フラグが継承されるかどうかは
前提にせず、常に
accept()
が返したソケットに対して全ての必要なフラグを明示的に設定するように
すべきである。
POSIX.1-2001 では
<sys/types.h>
のインクルードは必須とされておらず、
Linux
ではこのヘッダーファイルは必要ではない。
しかし、歴史的には、いくつかの実装
(BSD 系)
でこのヘッダーファイルが
必要であり、移植性が必要なアプリケーションではこのファイルを
インクルードするのが賢明であろう。
SIGIO
が届けられた後や、
select(2),
poll(2),
epoll(7)
が読み込み可能イベントを返した後に、
必ずしも待機中の接続があるとは限らない。
なぜならその接続は、
accept()
が呼ばれる前に、非同期的なネットワークエラーや
他のスレッドから呼ばれた
(別の) accept によって
削除されているかもしれないからである。
この場合、その
accept()
呼び出しは停止 (block)
し、次の接続の到着を待ちつづける。
accept()
に停止を行わせないようにするには、引数に渡すソケット
sockfd に
O_NONBLOCK
フラグをセットしておく必要がある
(
socket(7) を見よ)。
明示的な接続確認 (confirmation)
を必要とするようなプロトコル
(DECnet など) では、
accept()
は単に次の接続要求をキューから取り出すだけであり、
接続確認は行わないことに注意せよ。接続確認は、
新しいファイルディスクリプターに対する
通常の読み取り/書き込みによってなされ、接続拒否
(rejection)
は新しいソケットをクローズすることによってなされる。
現在のところ、 Linux
上でこれらのセマンティクスを持つのは
DECnet だけである。
In the original BSD sockets implementation (and on other older systems) the
third argument of
accept() was declared as an
int *. A
POSIX.1g draft standard wanted to change it into a
size_t *C;
later POSIX standards and glibc 2.x have
socklen_t * .
bind(2) 参照。
bind(2),
connect(2),
listen(2),
select(2),
socket(2),
socket(7)
この man ページは Linux
man-pages
プロジェクトのリリース
5.10
の一部である。プロジェクトの説明とバグ報告に関する情報は
https://www.kernel.org/doc/man-pages/
に書かれている。