关闭vs关闭套接字?

199 c sockets networking

在C中,我理解如果我们关闭套接字,则意味着套接字将被销毁,以后可以重新使用.

关机怎么样?描述说它关闭了与该套接字的双工连接的一半.但是那个套接字会像close系统调用一样被销毁吗?

Mat*_*hen 179

这在Beej的网络指南中有解释. shutdown是一种在一个或两个方向上阻止通信的灵活方式.当第二个参数是时SHUT_RDWR,它将阻止发送和接收(如close).但是,close是实际销毁套接字的方法.

有了shutdown,您仍然可以接收同伴已经发送的待处理数据(感谢Joey Adams注意到这一点).

  • 关闭套接字和关闭套接字之间的巨大差异是套接字被其他进程共享时的行为.shutdown()影响套接字的*all*副本,而close()仅影响一个进程中的文件描述符. (89认同)
  • -1.这篇文章和链接都省略了一个重要的概念性理由,希望使用`shutdown`:向对等体发送EOF信号,并且仍然能够接收对等体发送的待处理数据. (45认同)
  • 有关TIME_WAIT的评论不正确.这适用于端口,而不适用于套接字.你不能重用套接字. (23认同)
  • 请记住,即使你关闭()TCP套接字,它也不一定会立即重复使用,因为它将处于TIME_WAIT状态,而操作系统确保没有未完成的数据包可能会被混淆为新信息,如果你是立即重用该套接字的其他东西. (20认同)
  • @MatthewFlaschen:这只适用于你使用`shutdown(SD_SEND)`,因为`shutdown(SD_BOTH)`会导致后续`recv`s失败(尽管快速测试显示某些`recv'可能会成功接收是关闭的,因为字节是缓冲的).我将downvote更改为upvote. (5认同)
  • 你可能想要使用`shutdown`两个方向而不是'close`的一个原因是你使用`fdopen`对套接字进行`FILE`引用.如果你'关闭'套接字,一个新打开的文件可以分配相同的fd,随后使用`FILE`将读/写错误的地方,这可能是非常糟糕的.如果你只是'shutdown`,那么`FILE`的后续使用只会给出错误,直到调用`fclose`为止. (4认同)
  • @SergeyL.我在第一次评论中说的是错的.我测试了它,你实际上必须"关闭"套接字以清除文件描述符表中的插槽.`shutdown`两种方式都不*做*. (3认同)
  • 正确.如果仅关闭在套接字上写入,您仍然可以在其上接收数据,如果仅关闭读取,您仍然可以在其上发送数据.只有当它被关闭时,它才会被破坏.(但是在使用`shutdown`来禁用读取和写入之后调用`close(fd)`是不必要的,无论你是在一两次调用中都这样做.) (2认同)
  • 我必须纠正自己:我的印象是,两种方式关闭套接字相当于在它上面调用"close",但我只是尝试过它,但事实并非如此.文件描述符保持有效(不可重用),直到你调用`close`.我不确定*使用*关闭双向套接字是什么,但也许除了关闭它之外还可以用它做什么. (2认同)

Ear*_*ine 122

现有的答案都不告诉人们如何shutdown以及close在TCP协议级别的作品,所以它是值得添加此.

标准TCP连接通过4路终结终止:

  1. 一旦参与者没有更多数据要发送,它就会向另一个发送FIN数据包
  2. 另一方返回FIN的ACK.
  3. 当另一方也完成数据传输时,它会发送另一个FIN数据包
  4. 初始参与者返回ACK并完成传输.

但是,还有另一种"紧急"方式来关闭TCP连接:

  1. 参与者发送RST数据包并放弃连接
  2. 另一方收到一个RST,然后放弃连接

在我使用Wireshark进行的测试中,使用默认套接字选项,shutdown将FIN数据包发送到另一端,但就是这样.在另一方向您发送FIN数据包之前,您仍然可以接收数据.一旦发生这种情况,您Receive将获得0大小的结果.因此,如果您是第一个关闭"发送"的人,则应在完成数据接收后关闭套接字.

另一方面,如果close在连接仍处于活动状态时进行呼叫(另一端仍然处于活动状态,并且系统缓冲区中可能还有未发送的数据),则会将RST数据包发送到另一端.这有利于错误.例如,如果您认为对方提供了错误的数据或拒绝提供数据(DOS攻击?),您可以立即关闭套接字.

我对规则的看法是:

  1. 考虑shutdown之前close可能当
  2. 如果在决定关闭之前完成了接收(接收的0大小数据),请在最后一次发送(如果有)完成后关闭连接.
  3. 如果要正常关闭连接,请关闭连接(使用SHUT_WR,如果您不关心在此之后接收数据,也使用SHUT_RD),并等待直到收到0大小的数据,然后关闭插座.
  4. 在任何情况下,如果发生任何其他错误(例如超时),只需关闭套接字即可.

SHUT_RD和SHUT_WR的理想实现

以下未经过测试,信任风险自负.但是,我相信这是一种合理而实用的做事方式.

如果TCP堆栈仅通过SHUT_RD接收关闭,则应将此连接标记为不再需要数据.任何挂起和后续read请求(无论它们处于哪个线程)都将返回零大小的结果.但是,连接仍然是活动的和可用的 - 例如,您仍然可以接收OOB数据.此外,操作系统将删除它为此连接接收的任何数据.但就是这样,没有包裹会被发送到另一方.

如果TCP堆栈仅通过SHUT_WR接收关闭,则应标记此连接,因为不能再发送数据.所有挂起的写入请求都将完成,但后续写入请求将失败.此外,FIN数据包将被发送到另一侧以通知他们我们没有更多数据要发送.

  • @EJP不,这不是同义反复.您可以`shutdown()`连接然后它不再存在.你仍然有文件描述符.你仍然可以从接收缓冲区中"recv()".你仍然需要调用`close()`来处理文件描述符. (4认同)
  • @PavelŠimerda是的TCP并不表示不期待更多的数据.这应该在高级协议中考虑.在我看来,这一般不是必需品.你可以关上门,但你不能阻止人们把礼物放在门前.这是他们的决定,而不是你的决定. (3认同)
  • "如果在连接仍处于活动状态时调用close"则是同义反复,并且不会导致发送RST.(1)没有必要.在(4)中,超时不一定对连接是致命的,并且不会不变表明您可以关闭它. (2认同)

Mil*_*lan 35

close()如果使用shutdown()相反的话,可以避免一些限制.

close()将终止TCP连接上的两个方向.有时您想告诉另一个端点您已完成发送数据,但仍希望接收数据.

close()递减描述符引用计数(在文件表条目中维护并计算当前打开的引用文件/套接字的描述符的数量),如果描述符不为0则不关闭套接字/文件.这意味着如果要分配,只有在引用计数降为0后才会进行清理.shutdown()一个可以启动正常的TCP关闭序列,忽略引用计数.

参数如下:

int shutdown(int s, int how); // s is socket descriptor
Run Code Online (Sandbox Code Playgroud)

int how 可:

SHUT_RD0 进一步接收是不允许的

SHUT_WR或者1 不允许进一步发送

SHUT_RDWR或者2 不允许进一步发送和接收

  • 你不能'使用关闭'.您也可以使用它*.但是您必须关闭套接字一段时间. (23认同)
  • 这两个功能用于完全不同的目的.如果尚未启动套接字上的最终关闭,则启动关闭序列的事实不会改变close用于清除套接字数据结构的事实,而关闭用于启动tcp级别关闭序列. (8认同)

Len*_*ate 15

这可能是平台特定的,我不知何故怀疑它,但无论如何,我见过的最好的解释是在这个msdn页面上,他们解释了关闭,延迟选项,套接字闭包和一般连接终止序列.

总之,使用shutdown在TCP级别发送关闭序列,并使用close来释放进程中套接字数据结构使用的资源.如果在调用close时没有发出明确的关闭序列,则会启动一个关闭序列.

  • 任何试图实现 HTTP 连接正常关闭的人可能都想阅读关于“lingering close”的古老 Apache 注释,可以在 http://cluster.cis.drexel.edu/manual/misc/perf-tuning.html 等地方找到,也许还有更现代的评论:https://apenwarr.ca/log/20090814 (2认同)

Tob*_*oby 8

我也在Linux下使用shutdown()一个pthread成功强制另一个当前被阻止的pthread connect()提前中止.

在其他操作系统(至少OSX)下,我发现调用close()足以connect()导致失败.


小智 7

"shutdown()实际上并没有关闭文件描述符 - 它只是改变了它的可用性.要释放套接字描述符,你需要使用close()." 1