accept() - Unix,Linux 系统调用
广告
名称
accept - 接受套接字上的连接
概要
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
描述
accept() 系统调用用于基于连接的套接字类型(SOCK_STREAM、SOCK_SEQPACKET)。它从挂起的连接队列中提取第一个连接请求,创建一个新的已连接套接字,并返回一个引用该套接字的新文件描述符。新创建的套接字不是处于监听状态。原始套接字 sockfd不受此调用的影响。
参数 sockfd是一个使用socket(2)创建的套接字,使用bind(2)绑定到本地地址,并在listen(2)之后监听连接。
参数 addr是指向sockaddr结构的指针。此结构中填充了对等套接字的地址,如通信层所知。返回的addr地址的确切格式由套接字的地址族决定(参见socket(2)和相应的协议手册页)。
addrlen参数是一个值结果参数:它最初应包含addr指向的结构的大小;返回时,它将包含返回地址的实际长度(以字节为单位)。当addr为NULL时,不会填充任何内容。
如果队列中没有挂起的连接,并且套接字未标记为非阻塞,则accept() 将阻塞调用者,直到出现连接。如果套接字标记为非阻塞且队列中没有挂起的连接,则accept()将失败并返回错误 EAGAIN。
为了接收套接字上的传入连接通知,可以使用select(2)或poll(2)。当尝试新的连接时,将传送可读事件,然后可以调用accept()来获取该连接的套接字。或者,可以将套接字设置为在套接字上发生活动时传递SIGIO;有关详细信息,请参见socket(7)。
对于某些需要显式确认的协议(例如 DECNet),accept() 可以被认为只是将下一个连接请求出队,而并不意味着确认。确认可以通过对新文件描述符的正常读或写来隐含,拒绝可以通过关闭新套接字来隐含。目前只有 DECNet 在 Linux 上具有这些语义。
注释
在传递SIGIO或select(2)或poll(2)返回可读事件后,并不总是有连接等待,因为连接可能在调用accept()之前被异步网络错误或其他线程删除。如果发生这种情况,则调用将阻塞,等待下一个连接到达。
为确保accept()永不阻塞,传递的套接字sockfd需要设置O_NONBLOCK标志(参见socket(7))。
返回值
成功时,accept()返回一个非负整数,它是已接受套接字的描述符。出错时,返回 -1,并相应设置errno。
错误处理
Linux accept()将新套接字上已挂起的网络错误作为accept()的错误代码传递。此行为与其他 BSD 套接字实现不同。为了可靠地操作,应用程序应在accept()之后检测为协议定义的网络错误,并像EAGAIN一样通过重试来处理它们。对于 TCP/IP,这些是ENETDOWN、EPROTO、ENOPROTOOPT、EHOSTDOWN、ENONET、EHOSTUNREACH、EOPNOTSUPP和ENETUNREACH。
错误
如果出现以下情况,accept()将失败:
标签 | 描述 |
EAGAIN 或 EWOULDBLOCK | 套接字标记为非阻塞,并且没有连接可以接受。 |
EBADF | 描述符无效。 |
ECONNABORTED | 连接已中止。 |
EINTR | 系统调用被在有效连接到达之前捕获的信号中断。 |
EINVAL | 套接字未监听连接,或addrlen无效(例如,为负数)。 |
EMFILE | 已达到打开文件描述符的每个进程限制。 |
ENFILE | 已达到打开文件的系统限制总数。 |
ENOTSOCK | 描述符引用的是文件,而不是套接字。 |
EOPNOTSUPP | 引用的套接字不是SOCK_STREAM类型。 |
如果出现以下情况,accept()可能会失败:
标签 | 描述 |
EFAULT | addr参数不在用户地址空间的可写部分。 |
ENOBUFS、ENOMEM | 可用内存不足。这通常意味着内存分配受套接字缓冲区限制,而不是系统内存的限制。 |
EPROTO | 协议错误。 |
如果出现以下情况,Linux accept()可能会失败:
此外,新套接字的网络错误以及协议定义的网络错误可能会被返回。各种 Linux 内核可能会返回其他错误,例如ENOSR、ESOCKTNOSUPPORT、EPROTONOSUPPORT、ETIMEDOUT。在跟踪期间可能会看到值ERESTARTSYS。
符合标准
SVr4、4.4BSD(accept() 首次出现在 4.2BSD 中)。
注释
accept()的第三个参数最初声明为“int *”(在 libc4 和 libc5 以及许多其他系统(如 4.x BSD、SunOS 4、SGI)下都是如此);POSIX.1g 草案标准希望将其更改为“size_t *”,这就是 SunOS 5 的情况。后来的 POSIX 草案使用“socklen_t *”,Single Unix Specification 和 glibc2 也是如此。
参见
广告
|