【问题标题】:close() socket directly after send(): unsafe?在发送()之后直接关闭()套接字:不安全?
【发布时间】:2012-02-11 00:58:35
【问题描述】:

在最后一个send() 之后直接close() 一个套接字是否明智/安全?

我知道即使在关闭套接字之后,TCP 也应该尝试传递发送缓冲区中的所有剩余数据,但我真的可以指望吗?

我正在确保我的接收缓冲区中没有剩余数据,以便在我关闭后不会发送 RST。


就我而言,close 实际上是调用exit() 之前的最后一条代码语句。

即使在发送数据的进程终止后,TCP 堆栈是否真的会继续尝试传输数据?这是否像在通过设置 SO_LINGER 调用 close() 之前自己等待任意超时一样可靠?

也就是说,是否应用相同的 TCP 超时,还是更短?毕竟,由于发送缓冲区较大且连接速度较慢,因此实际传输所有缓冲数据的时间可能会很长。


我对收到最后一个字节发送的通知不感兴趣;我只是希望它们最终尽可能可靠地到达远程主机。

应用层确认不是一个选项(协议是 HTTP,我正在编写一个小型服务器)。

【问题讨论】:

  • 这取决于实现,但一般来说,是的,close() 将在实际拆除套接字之前刷新缓冲区中剩余的任何数据。如果您在调用close() 之外终止整个过程,里程可能会有所不同。
  • 如果您退出流程,@aroth 里程将发生变化。这两种情况没有区别。如果您想保持相反的情况,请提供权威参考。
  • @EJP - 关于在任意程序中可能发生什么的权威参考?我不认为这样的事情存在。但这很简单,可以举一个似是而非的例子。假设您有一个程序将所有网络操作委托给一个单独的线程,就像大多数程序一样。当后台线程调用close() 时,主线程可能会终止进程(优雅地或以其他方式)。在这种情况下调用会成功吗?我对此表示怀疑。当然,您所说的对于任何单线程程序都应该成立。但并非所有程序都是单线程的。
  • @aroth 不,权威参考,说明您所说的“里程可能会有所不同”。你不会找到一个。套接字关闭是应用程序退出时由应用程序或内核完成的套接字关闭。如果您有反例,请再次提供。

标签: sockets tcp


【解决方案1】:

应用层确认不是一个选项(协议是 HTTP,我正在编写一个小型服务器)。

HTTP 协议不存在这个问题。 HTTP 服务器不应该在任何正常操作中关闭连接。客户端在recv() 之后关闭它,它确切地知道它需要多少字节。

为了清楚起见,答案是“不”。

【讨论】:

  • 这不太正确。到目前为止,所有 HTTP 标准都包括服务器启动关闭连接的情况。例如,datatracker.ietf.org/doc/html/rfc7230#section-6.6 或多或少描述了 selbie 的回答中的过程。
  • 没错。服务器可以启动连接的关闭过程,让客户端真正关闭它。在任何情况下,客户端都会在读取响应后关闭套接字。
  • 啊哈,所以关闭是指最后一个关闭的派对。在那种情况下,你是对的。客户端始终是最后发送 FIN 消息的一方。
【解决方案2】:

我已经阅读了很多 The ultimate SO_LINGER page, or: why is my tcp not reliable 博客文章。我建议你也读一下。它讨论了与 TCP 套接字有关的大数据传输的边缘情况。

我不是SO_LINGER 的专家,但在我的服务器代码(仍在积极开发中)我执行以下操作:

  1. 通过send()发送最后一个字节后,我调用shutdown(sock, SHUT_WR)触发发送FIN。

  2. 然后等待随后对该套接字的 recv() 调用返回 0(或 recv 返回 -1 并且 errno 是除 EAGAIN/EWOULDBLOCK 之外的任何值)。

  3. 然后服务器在socket上做一个close()

假设客户端在收到响应的所有字节后会首先关闭他的套接字。

但我确实在最终的 send()recv() 指示 EOF 之间强制执行了超时。如果客户端从不关闭连接,服务器将放弃等待并关闭连接。我的超时时间为 45-90 秒。

我所有的套接字都是非阻塞的,我使用 poll/epoll 来通知连接事件作为提示,看看是否该再次尝试调用 recv()send()

【讨论】:

  • 这似乎是一个非常好的解决方案,只要客户端先关闭套接字。但是,他不能在发送请求后立即写关闭他的一半连接吗? HTTP 规范没有说明半关闭的问题。您认为这种行为曾经发生在 HTTP 客户端中吗?
  • 没有。我做了一些特别的wireshark嗅探来验证。我鼓励你也这样做。根据我的数据包嗅探测试 - 有时服务器在响应后发送其 FIN,而客户端在收到响应后发送 FIN。但是大多数 http 客户端无论如何都会为服务器指定“keep-alive”选项,因此客户端无论如何都不会启动关闭。换句话说,我认为假设客户不会半关闭是非常安全的。
  • 此外,客户可能会关注 Content-Length 标头。服务器在发送所有字节后不需要关闭或关闭服务器可能是合理的,因为客户端知道在 HTTP 响应中期望多少字节。 (如果它支持连接保持活动并且如果客户端请求它,服务器肯定不会关闭)。但是在没有keep-alive的简单请求中,服务器在他的响应发送后发送一个FIN。
  • 好的,所以我将使用那个非常漂亮的 linger 页面上描述的方法,谢谢你的链接!似乎任何进行半关闭的客户都必须承受后果。我也从未见过客户端这样做,但像docs.python.org/howto/sockets.html 这样的地方特别建议将半关闭作为执行 HTTP(类似协议)的有效方式,所以我认为这种行为相当普遍。
  • 我已经尝试过了,无论客户端在请求后立即发送 FIN 还是在完成传输后才发送 FIN,它都可以正常工作。 (在第一种情况下,进程将在完成传输之前终止,但 TCP 堆栈将完成现在孤立套接字的传输。这可能会超时,但在我的实验中,它保持活动超过 10 分钟.)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-21
  • 2016-04-14
  • 2016-10-08
  • 1970-01-01
相关资源
最近更新 更多