>问题
有一个线程在循环中的阻塞 UDP 套接字上运行recv
。我们需要停止该线程并关闭该套接字。
例:
#include <unistd.h>
#include <sys/socket.h>
#include <thread>
#include <atomic>
std::atomic<int> the_socket;
void threadf() {
for(;;) {
char c;
int ret = recv(the_socket, &c, 1, 0);
if(ret == 0 || ret == -1) {
break;
}
}
close(the_socket);
}
int main() {
the_socket = socket(AF_INET, SOCK_DGRAM, 0);
std::thread *t = new std::thread(threadf);
usleep(200000);
shutdown(the_socket, SHUT_RDWR); // ENOTCONN
t->join();
delete t;
return 0;
}
描述:
- 它创建 UDP 套接字
- 它启动另一个线程,同步接收来自该套接字的数据包。
- 主线程决定是时候停止了。
delete t
是个坏主意。close(the_socket)
不会解锁相邻线程。但是shutdown
虽然本身失败了,但完成了这项工作:
[pid 22289] nanosleep({tv_sec=0, tv_nsec=200000000}, <unfinished ...>
[pid 22290] <... set_robust_list resumed> ) = 0
[pid 22290] recvfrom(3, <unfinished ...>
[pid 22289] <... nanosleep resumed> NULL) = 0
[pid 22289] shutdown(3, SHUT_RDWR) = -1 ENOTCONN (Transport endpoint is not connected)
[pid 22290] <... recvfrom resumed> "", 1, 0, NULL, NULL) = 0
问题
该方案的便携性和可靠性如何?不,根本不便携。多线程环境中不。shutdown
的确切行为是否记录在某处?不。shutdown
对非连接/UDP 套接字的影响是否记录在某处?- 是否可以对套接字执行其他操作以可靠且便携地中断相邻线程上的
recv
?
更新:似乎在Mac上失败了。 然而close
在那里工作...
仅给定 UDP 套接字本身,唤醒线程的唯一真正可移植的选项是:
-
将套接字切换到非阻塞模式,然后让线程使用
select()
或(e)poll()
来检测何时准备好从套接字读取入站数据包。 这样,您可以在每次等待时指定超时,线程可以在两次等待之间检查终止。 -
使用
sendto()
将专用 UDP 数据包发送到套接字的侦听端口以唤醒阻塞recv()
/recvfrom()
调用,然后线程将可以自由检查终止。请注意,如果您使用connect()
将 UDP 套接字与对等 IP/端口静态关联,这将不起作用,因为这样做会限制recv()
/recvfrom()
从其他来源接收任何数据包。 在您提供的示例中不是这种情况,但您应该注意这一点。
在许多平台上,您可以通过setsockopt(SO_RCVTIMEO)
使用阻塞读取超时,然后您可以在每次读取时指定超时,线程可以在两次读取之间检查终止。 但并非所有平台都支持SO_RCVTIMEO
.
否则,您可以打开第二个 UDP 套接字或管道,并让线程使用select()
或(e)poll()
同时在主 UDP 套接字旁边监视它。 不要在主套接字上调用recv()
/recvfrom()
,除非实际上有东西要从中读取。 当您要唤醒线程时,连接/发送到第二个管道/套接字。