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