【问题标题】:close vs shutdown socket?关闭与关闭套接字?
【发布时间】:2011-05-08 19:16:18
【问题描述】:

在C语言中,我理解如果我们关闭一个套接字,就意味着该套接字将被销毁,以后可以重新使用。

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

【问题讨论】:

  • 以后不能重复使用。它关门了。完成的。完成。

标签: c sockets networking


【解决方案1】:

linux: shutdown() 导致监听线程 select() 唤醒并产生错误。关掉();关闭();会导致无休止的等待。

winsock:反之亦然 - shutdown() 无效,而 close() 成功捕获。

【讨论】:

    【解决方案2】:

    这是 Beej 网络指南中的 explainedshutdown 是一种灵活的方式来阻止一个或两个方向的通信。当第二个参数为SHUT_RDWR 时,会同时阻塞发送和接收(如close)。但是,close 是实际销毁套接字的方式。

    使用shutdown,您仍然可以接收对等方已发送的待处理数据(感谢 Joey Adams 注意到这一点)。

    【讨论】:

    • 请记住,即使您 close() 一个 TCP 套接字,它也不一定可以立即重用,因为它将处于 TIME_WAIT 状态,而操作系统会确保没有未完成的数据包如果您要立即将该套接字用于其他用途,可能会因为新信息而感到困惑。
    • 关闭和关闭套接字的最大区别在于套接字被其他进程共享时的行为。 shutdown() 影响套接字的所有 个副本,而 close() 仅影响一个进程中的文件描述符。
    • 您可能希望在两个方向都使用shutdown 而不是close 的一个原因是,如果您使用fdopen 对套接字进行了FILE 引用。如果你 close 套接字,一个新打开的文件可能会被分配相同的 fd,随后使用 FILE 将读/写错误的地方,这可能是非常糟糕的。如果您只是 shutdown,则后续使用 FILE 只会出错,直到调用 fclose
    • 关于TIME_WAIT的评论不正确。这适用于端口,而不是套接字。您不能重复使用套接字。
    • -1。这篇文章和链接都省略了一个重要的概念原因,即要使用shutdown:向对等方发出 EOF 信号,并且仍然能够接收对等方发送的待处理数据。
    【解决方案3】:

    现有的答案都没有告诉人们shutdownclose 在 TCP 协议级别是如何工作的,因此值得添加。

    一个标准的 TCP 连接被 4-way finalization 终止:

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

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

    1. 参与者发送 RST 数据包并放弃连接
    2. 对方收到 RST 后也放弃连接

    在我对 Wireshark 的测试中,使用默认套接字选项,shutdown 向另一端发送一个 FIN 数据包,但这就是它所做的一切。直到对方向你发送 FIN 数据包,你仍然能够接收数据。一旦发生这种情况,您的 Receive 将获得 0 大小的结果。因此,如果您是第一个关闭“发送”的人,则应在完成接收数据后关闭套接字。

    另一方面,如果您在连接仍处于活动状态时调用close(另一端仍处于活动状态,并且您可能在系统缓冲区中也有未发送的数据),则会向另一端发送一个 RST 数据包边。这对错误有好处。例如,如果您认为对方提供了错误的数据或拒绝提供数据(DOS 攻击?),您可以立即关闭套接字。

    我对规则的看法是:

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

    SHUT_RD 和 SHUT_WR 的理想实现

    以下内容尚未经过测试,请自行承担风险。但是,我相信这是一种合理且实用的做事方式。

    如果 TCP 堆栈仅接收到带有 SHUT_RD 的关闭,则应将此连接标记为不再需要数据。任何未决和后续的read 请求(无论它们在哪个线程中)都将返回大小为零的结果。但是,连接仍然处于活动状态且可用——例如,您仍然可以接收 OOB 数据。此外,操作系统将丢弃它为此连接接收到的任何数据。但仅此而已,不会有任何包裹寄到对方。

    如果 TCP 堆栈仅接收到 SHUT_WR 关闭,它将标记此连接,因为无法发送更多数据。所有挂起的写请求都将完成,但后续的写请求将失败。此外,将向另一端发送一个 FIN 数据包,通知他们我们没有更多数据要发送。

    【讨论】:

    • “如果您在连接仍然存在时调用 close”是重言式,并且不会导致发送 RST。 (1) 没有必要。在 (4) 中,超时对连接不一定是致命的,并且并不总是表明您可以关闭它。
    • @EJP 不,这不是同义反复。您可以shutdown() 连接,然后它不再存在。您仍然拥有文件描述符。您仍然可以从接收缓冲区中recv()。而且你仍然需要调用close() 来处理文件描述符。
    • 这是关于电汇格式的最佳答案。我会对有关使用 SHUT_RD 关闭的更多详细信息感兴趣。没有 TCP 信令不期望更多数据,对吧?不发送更多数据的信号不是只有 FIN 吗?
    • @PavelŠimerda 是的,TCP 不会因为不期望更多数据而发出信号。这应该在高级协议中考虑。在我看来,这通常没有必要。你可以关上门,但你不能阻止人们在门前放礼物。这是他们的决定,不是你的。
    • @EJP 你有什么实际的论据吗?否则我不感兴趣。
    【解决方案4】:

    关闭

    当你使用完一个套接字后,你可以简单地用 close 关闭它的文件描述符;如果仍有数据等待通过连接传输,则正常关闭尝试完成此传输。您可以使用 SO_LINGER 套接字选项来控制此行为以指定超时时间;请参阅套接字选项。

    关机

    您也可以通过调用shutdown来仅关闭连接上的接收或传输。

    shutdown函数关闭socket的连接。它的参数 how 指定要执行的操作: 0 停止接收此套接字的数据。如果有更多数据到达,则拒绝它。 1 停止尝试从此套接字传输数据。丢弃任何等待发送的数据。停止寻找已发送数据的确认;如果丢失,请勿重新传输。 2 停止接收和发送。

    成功时返回值为0,失败时返回-1。

    【讨论】:

      【解决方案5】:

      在我的测试中。

      close在socket不与其他进程共享时会立即发送fin包并销毁fd

      shutdown SHUT_RD,进程仍然可以从套接字接收数据,但是如果 TCP 缓冲区为空,recv 将返回 0。当对端发送更多数据后,recv 将再次返回数据.

      shutdown SHUT_WR 将发送 fin 数据包以指示不允许进一步发送。对等方可以接收数据,但如果其 TCP 缓冲区为空,它将接收 0

      shutdown SHUT_RDWR(等于同时使用 SHUT_RDSHUT_WR)如果对等方发送更多数据,将发送第一个数据包。

      【讨论】:

      • 我刚刚试了一下,close() 在 Linux 上发送了 RST 而不是 FIN。
      • 另外你真的要声明关闭阅读端后程序会收到更多数据吗?
      • @PavelŠimerda 我认为发送 RST 或 FIN 可能取决于 tcp 状态?我不确定。
      • 1. “对等方发送更多数据后,recv() 将再次返回数据”不正确。 2、在SHUT_RD之后peer发送更多数据的行为是平台相关的。
      • @EJP 我自己试过了,对不起,我忘记了我做这个测试的平台,centos 还是 Mac。这就是我得到的
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        close() 有一些限制,如果使用shutdown() 代替,可以避免这些限制。

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

        close() 减少描述符引用计数(保存在文件表条目中并计算当前打开的引用文件/套接字的描述符数)并且如果描述符不为 0,则不会关闭套接字/文件。这意味着如果您正在分叉,则只有在引用计数降至 0 后才会进行清理。使用shutdown() 可以启动正常的 TCP 关闭序列,而忽略引用计数。

        参数如下:

        int shutdown(int s, int how); // s is socket descriptor
        

        int how 可以是:

        SHUT_RD0 不允许进一步接收

        SHUT_WR1 不允许进一步发送

        SHUT_RDWR2 不允许进一步发送和接收

        【讨论】:

        • 这两个函数的用途完全不同。如果套接字上的最终关闭启动了一个关闭序列(如果尚未启动),这一事实不会改变关闭是用于清理套接字数据结构而关闭是用于启动 tcp 级别的关闭序列的事实。跨度>
        • 您不能“改用关机”。你也可以使用它但你必须关闭套接字一段时间。
        【解决方案8】:

        这可能是特定于平台的,我对此表示怀疑,但无论如何,我见过的最好的解释是 here on this msdn page,他们解释了关闭、逗留选项、套接字关闭和一般连接终止序列。

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

        【讨论】:

        【解决方案9】:

        我在 linux 下也取得了成功,使用来自一个 pthread 的 shutdown() 强制当前在 connect() 中阻塞的另一个 pthread 提前中止。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-17
          • 2013-02-17
          • 2011-01-26
          • 2016-07-27
          • 1970-01-01
          相关资源
          最近更新 更多