当同一套接字上正在进行发送/接收时,可以从另一个线程关闭套接字吗?

发布于 2024-09-16 11:41:47 字数 163 浏览 4 评论 0原文

当同一套接字上正在进行发送/接收时,可以从另一个线程关闭套接字吗?

假设一个线程正在阻塞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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

≈。彩虹 2024-09-23 11:41:47

在 Linux 中,关闭套接字不会唤醒 recv()。另外,正如 @jxh 所说:

当套接字关闭时,如果线程在recv()或send()上被阻塞
如果由不同的线程执行,则被阻塞的线程将收到错误。
然而,事后很难发现正确的补救措施
收到错误。这是因为文件描述符号
与套接字关联的可能已被另一个不同的
线程,并且被阻塞的线程现在已因错误而被唤醒
“有效”套接字。在这种情况下,唤醒的线程不应调用
close() 本身。

被唤醒的线程需要某种方式来区分是否
错误是由连接生成的(例如网络错误)
要求它调用 close(),或者如果错误是由
不同的线程调用了 close() ,在这种情况下它应该
只是出错而不对套接字做任何进一步的事情。

因此,避免这两个问题的最佳方法是调用 shutdown() 而不是 close()shutdown() 将使文件描述符仍然可用,因此不会被另一个描述符分配,也会以错误和线程唤醒 recv() recv() 调用可以以正常方式关闭套接字,就像发生正常错误一样。

In linux closing a socket won't wake up recv(). Also, as @jxh says:

If a thread is blocked on recv() or send() 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 call
close() 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 called close() on it, in which case it should
just error out without doing anything further to the socket.

So the best way to avoid both problems is to call shutdown() instead of close(). shutdown() will make the file descriptor still available, so won't be allocated by another descriptor, also will wake up recv() with an error and the thread with the recv() call can close the socket the normal way, like a normal error happened.

冷︶言冷语的世界 2024-09-23 11:41:47

我不知道 Solaris 网络堆栈的实现,但我会放弃我的理论/解释为什么它应该是安全的。

  • 对于这个给定的套接字,线程 A 进入某个阻塞系统调用,例如 read(2)。套接字接收缓冲区中没有数据,因此线程 A 从处理器中取出并放入该套接字的等待队列中。这里没有发起网络堆栈事件,连接状态(假设 TCP)没有改变。
  • 线程 B 在套接字上发出close(2)。虽然内核套接字结构在线程 B 访问它时应该被锁定,但没有其他线程持有该锁(线程 A 在进入睡眠等待状态时释放了该锁)。假设套接字发送缓冲区中没有未完成的数据,则会发送一个FIN数据包,并且连接进入FIN WAIT 1状态(我再次假设这里是TCP,请参阅连接状态图
  • 我猜测套接字连接状态更改将为给定套接字上阻塞的所有线程生成唤醒。也就是说线程A会进入可运行状态并发现连接正在关闭。如果对方没有发送自己的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.

  • Thread A enters some blocking system call, say 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.
  • Thread B issues 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, a FIN packet is sent and the connection enters the FIN WAIT 1 state (again I assume TCP here, see connection state diagram)
  • I'm guessing that socket connection state change would generate a wakeup for all threads blocked on given socket. That is thread A would enter a runnable state and discover that connection is closing. The wait might be re-entered if the other side has not sent its own FIN, or the system call would return with eof 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.

2024-09-23 11:41:47

对我来说,来自另一个线程的 shutdown() 套接字在 Linux 中完成了这项工作

For me, shutdown() socket from another thread does the job in Linux

桃扇骨 2024-09-23 11:41:47

如果当套接字被其他线程关闭时,线程在 recv()send() 上被阻塞,则被阻塞的线程将收到错误。然而,在收到错误后很难检测到正确的补救措施。这是因为与套接字关联的文件描述符号可能已被另一个线程拾取,并且阻塞的线程现在已因“有效”套接字的错误而被唤醒。在这种情况下,唤醒的线程不应该调用close()本身。

唤醒的线程需要某种方法来区分错误是由需要调用 close() 的连接生成的(例如网络错误),还是由其他线程生成的在其上调用了 close() ,在这种情况下,它应该只是出错,而不对套接字做任何进一步的操作。

If a thread is blocked on recv() or send() 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 call close() 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 called close() on it, in which case it should just error out without doing anything further to the socket.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文