【发布时间】:2010-08-28 06:24:37
【问题描述】:
当同一个套接字上的发送/接收正在进行时,可以从另一个线程关闭一个套接字吗?
假设一个线程正在阻塞recv调用,而另一个线程关闭了同一个socket,recv调用中的线程会知道这一点并安全出来吗?
我想知道不同操作系统/平台之间的行为是否会有所不同。如果是,它将在 Solaris 中表现如何?
【问题讨论】:
当同一个套接字上的发送/接收正在进行时,可以从另一个线程关闭一个套接字吗?
假设一个线程正在阻塞recv调用,而另一个线程关闭了同一个socket,recv调用中的线程会知道这一点并安全出来吗?
我想知道不同操作系统/平台之间的行为是否会有所不同。如果是,它将在 Solaris 中表现如何?
【问题讨论】:
在 linux 中关闭套接字不会唤醒 recv()。另外,正如@jxh 所说:
如果在套接字关闭时线程在 recv() 或 send() 上被阻塞 通过不同的线程,被阻塞的线程将收到错误。 但是,很难检测到正确的补救措施 收到错误。这是因为文件描述符编号 与套接字关联的可能已经被另一个不同的 线程,并且被阻塞的线程现在已经被一个错误唤醒了 “有效”套接字。在这种情况下,被唤醒的线程不应该调用 close() 本身。
被唤醒的线程需要一些方法来区分是否 错误是由连接产生的(例如网络错误) 要求它调用 close(),或者如果错误是由 不同的线程调用了 close() ,在这种情况下它应该 只是出错而不对套接字做任何进一步的事情。
因此,避免这两个问题的最佳方法是调用shutdown() 而不是close()。 shutdown() 将使文件描述符仍然可用,因此不会被另一个描述符分配,也会以错误唤醒 recv() 并且带有 recv() 调用的线程可以以正常方式关闭套接字,就像发生了正常错误。
【讨论】:
我不知道 Solaris 网络堆栈的实现,但我会抛出我的理论/解释为什么它应该是安全的。
read(2)。套接字接收缓冲区中没有数据,因此线程 A 从处理器中取出并放入此套接字的等待队列中。此处未启动网络堆栈事件,连接状态(假设为 TCP)未更改。close(2)。虽然在线程 B 访问内核套接字结构时应该锁定它,但没有其他线程持有该锁(线程 A 在进入睡眠等待时释放了锁)。假设套接字发送缓冲区中没有未完成的数据,则发送一个FIN 数据包并且连接进入FIN WAIT 1 状态(这里我再次假设TCP,参见connection state diagram)FIN,则可能会重新进入等待,否则系统调用将返回eof。在任何情况下,内部内核结构都将受到保护,不会受到不适当的并发访问。这并不意味着从多个线程执行套接字 I/O 是一个好主意。我建议研究非阻塞套接字、状态机和框架,如libevent。
【讨论】:
对我来说,来自另一个线程的 shutdown() 套接字在 Linux 中完成这项工作
【讨论】:
如果一个线程在recv() 或send() 上被阻塞,而套接字被另一个线程关闭,则被阻塞的线程将收到错误。然而,在收到错误后很难检测到正确的补救措施。这是因为与套接字关联的文件描述符编号可能已被另一个线程拾取,并且阻塞的线程现在已因“有效”套接字的错误而被唤醒。在这种情况下,被唤醒的线程应该不调用close()本身。
被唤醒的线程需要某种方式来区分错误是由需要它调用close()的连接产生的(例如网络错误),还是由调用@987654325的不同线程产生的@ 就可以了,在这种情况下,它应该只是出错而不对套接字做任何进一步的事情。
【讨论】:
是的,可以从另一个线程关闭套接字。任何使用该套接字的阻塞/忙碌线程都将报告适当的错误。
【讨论】: