当同一套接字上正在进行发送/接收时,可以从另一个线程关闭套接字吗?
当同一套接字上正在进行发送/接收时,可以从另一个线程关闭套接字吗?
假设一个线程正在阻塞recv调用,而另一个线程关闭同一个套接字,那么recv调用中的线程会知道这一点并安全地退出吗?
我想知道不同操作系统/平台之间的行为是否有所不同。如果是,它在 Solaris 中的表现如何?
Can a socket be closed from another thread when a send / recv on the same socket is going on?
Suppose one thread is in blocking recv call and another thread closes the same socket, will the thread in the recv call know this and come out safely?
I would like to know if the behavior will differ between different OS / Platforms. If yes, how will it behave in Solaris?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 Linux 中,关闭套接字不会唤醒
recv()
。另外,正如 @jxh 所说:因此,避免这两个问题的最佳方法是调用
shutdown()
而不是close()
。shutdown()
将使文件描述符仍然可用,因此不会被另一个描述符分配,也会以错误和线程唤醒recv()
recv()
调用可以以正常方式关闭套接字,就像发生正常错误一样。In linux closing a socket won't wake up
recv()
. Also, as @jxh says:So the best way to avoid both problems is to call
shutdown()
instead ofclose()
.shutdown()
will make the file descriptor still available, so won't be allocated by another descriptor, also will wake uprecv()
with an error and the thread with therecv()
call can close the socket the normal way, like a normal error happened.我不知道 Solaris 网络堆栈的实现,但我会放弃我的理论/解释为什么它应该是安全的。
read(2)
。套接字接收缓冲区中没有数据,因此线程 A 从处理器中取出并放入该套接字的等待队列中。这里没有发起网络堆栈事件,连接状态(假设 TCP)没有改变。close(2)
。虽然内核套接字结构在线程 B 访问它时应该被锁定,但没有其他线程持有该锁(线程 A 在进入睡眠等待状态时释放了该锁)。假设套接字发送缓冲区中没有未完成的数据,则会发送一个FIN
数据包,并且连接进入FIN WAIT 1
状态(我再次假设这里是TCP,请参阅连接状态图)FIN
,则可能会重新进入等待,否则系统调用将返回eof
。无论如何,内部内核结构都将受到保护,免受不适当的并发访问。这并不意味着从多个线程执行套接字 I/O 是个好主意。我建议研究非阻塞套接字、状态机和框架,例如
libevent
。I don't know Solaris network stack implementation but I'll throw out my theory/explanation of why it should be safe.
read(2)
, for this given socket. There's no data in socket receive buffer, so thread A is taken off the processor an put onto wait queue for this socket. No network stack events are initiated here, connection state (assuming TCP) has not changed.close(2)
on the socket. While kernel socket structure should be locked while thread B is accessing it, no other thread is holding that lock (thread A released the lock when it was put to sleep-wait). Assuming there's no outstanding data in the socket send buffer, aFIN
packet is sent and the connection enters theFIN WAIT 1
state (again I assume TCP here, see connection state diagram)FIN
, or the system call would return witheof
otherwise.In any case, internal kernel structures will be protected from inappropriate concurrent access. This does not mean it's a good idea to do socket I/O from multiple threads. I would advise to look into non-blocking sockets, state machines, and frameworks like
libevent
.对我来说,来自另一个线程的
shutdown()
套接字在 Linux 中完成了这项工作For me,
shutdown()
socket from another thread does the job in Linux如果当套接字被其他线程关闭时,线程在
recv()
或send()
上被阻塞,则被阻塞的线程将收到错误。然而,在收到错误后很难检测到正确的补救措施。这是因为与套接字关联的文件描述符号可能已被另一个线程拾取,并且阻塞的线程现在已因“有效”套接字的错误而被唤醒。在这种情况下,唤醒的线程不应该不调用close()
本身。唤醒的线程需要某种方法来区分错误是由需要调用 close() 的连接生成的(例如网络错误),还是由其他线程生成的在其上调用了
close()
,在这种情况下,它应该只是出错,而不对套接字做任何进一步的操作。If a thread is blocked on
recv()
orsend()
when the socket is closed by a different thread, the blocked thread will receive an error. However, it is difficult to detect the correct remedial action after receiving the error. This is because the file descriptor number associated with the socket may have been picked up by yet a different thread, and the blocked thread has now been woken up on an error for a "valid" socket. In such a case, the woken up thread should not callclose()
itself.The woken up thread will need some way to differentiate whether the error was generated by the connection (e.g. a network error) that requires it to call
close()
, or if the error was generated by a different thread having calledclose()
on it, in which case it should just error out without doing anything further to the socket.