将一个接收套接字设置为非阻塞模式是否可行(且安全)?

16
我正在寻找一种打断阻塞套接字上的 accept() 调用的方法。使用信号不是一个选项,因为这是一个库,我不想混淆用户信号。使用 select() 是另一种选择,但由于各种原因,在我的情况下并不是很有吸引力。
如果可能的话,一个好的解决方案是在另一个线程中将套接字设置为非阻塞模式(使用 fcntl()O_NONBLOCK),而套接字被阻塞在 accept() 调用上。预期行为是 accept() 调用将以 errno 中的 EAGAINEWOULDBLOCK 返回。
这样确实会起作用吗?它安全吗?可移植吗?
如果您了解此方法在Windows上的适用性(需要使用WSAIoctl()FIONBIO),我也很感兴趣。

fcntl(socket, F_SETFL, O_NONBLOCK); 对我很有帮助。 - Vlad Holubiev
2个回答

12

不清楚Windows,但POSIX可以保证您想要的行为:

如果连接请求的监听队列为空,并且套接字文件描述符上未设置O_NONBLOCK标志,则accept()将一直阻塞,直到有连接出现。如果连接请求的监听队列为空,并且套接字文件描述符上设置了O_NONBLOCK标志,则accept()将失败,并将错误代码设置为[EAGAIN]或[EWOULDBLOCK]。

来源:http://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html

此外,可以使用selectpoll通过在读取集合中轮询监听套接字来检查传入连接。


1
你确定如果已经阻塞的 accept() 调用在传递的文件描述符异步设置了 O_NONBLOCK 标志后会按照规定返回吗?对于这种情况,你的引用对我来说似乎有歧义。 - alk
3
哦,我误解了你的问题。不,我认为在阻塞模式下将其更改为非阻塞模式是不可移植的。为什么不一开始就将其设置为非阻塞并在accept调用之前使用pollselect,只有当有连接可用时才调用accept呢?有许多简洁的方法可以使selectpoll提早返回,比如自管道技巧。 - R.. GitHub STOP HELPING ICE
好的,明确的答案是它不可能。无论是在理论上还是实践中(我最终在Linux和Windows上都尝试了一下)。 - Norswap
在Windows上,您应该使用OVERLAPPED io。为了可移植性,我建议使用Boost Asio。 - doron
在Windows(WinSock)上,您实际上可以从另一个线程关闭套接字。这将解除在该套接字上阻塞的任何线程的阻塞。 - JamieB
@JamieB:这绝不是一种安全的操作,可能会导致在相同的fd号上打开其他东西,并且accept在被信号中断后可能会继续进行。 - R.. GitHub STOP HELPING ICE

1
在这个问题中,您说您不想使用选择(或轮询或epoll),它们是IO多路复用的最佳方式。我建议您使用另一个线程仅用于监听套接字,尽管这是一个不好的主意!

2
谢谢您的回答,但这个问题是8年前提出的。事实上,我甚至不记得当时我想要实现什么了! - Norswap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接