【问题标题】:What happened to socket if network has broken down如果网络出现故障,socket 会发生什么
【发布时间】:2012-10-10 03:46:34
【问题描述】:

假设一个简单的网络模型:A 已经成功地创建了一个到 B 的 TCP 连接,并且他们正在像这样相互通信

A <----------> B

我知道如果 A 上的程序死掉(例如核心转储),这将导致一个 RST 数据包到 B。所以 B 的任何读取尝试都会导致 EOF,而 B 的任何写入尝试都会导致 SIGPIPE .我说的对吗?

但是,如果假设 A 上的网络出现故障(例如电缆/路由器故障),那么 B 的读/写尝试会发生什么情况?在我的情况下,所有套接字都设置为非阻塞。这样一来,我是不是无法检测到网络错误?

顺便说一句,我注意到套接字中有一个选项SO_KEEPALIVE,它可能对我有用http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/。但我想知道如果我将探测间隔设置为 2~3 秒(默认为 75 秒),成本会是多少?而且似乎间隔配置是全局配置,那么这会影响机器上的所有套接字吗?

最后一个问题... 假设网络出现故障,任何写入尝试都会在一段时间后导致 EPIPE。但是,如果我不尝试写入,而是将此套接字放入 epoll 设备中,那么会发生什么? epoll_wait 会返回 EPOLLHUP 或 EPOLLERR 事件吗?

【问题讨论】:

  • 最后更新这个问题。添加 epoll 设备的新问题
  • 编辑 epoll() 操作的答案 : reff: Edited answer

标签: linux sockets network-programming keep-alive


【解决方案1】:

还有许多其他方式可以使 TCP 连接失效而未被检测到

  • 有人拔出中间的网线。
  • 另一端的计算机被核爆了。
  • 中间的 nat 网关静默断开连接
  • 另一端的操作系统严重崩溃。
  • FIN 数据包丢失。
  • 不可检测的错误:端点之间的路由器可能会丢弃数据包。(包括控制数据包) reff

在所有情况下,您都可以通过程序中的 SIGPIPE 错误尝试在套接字上写入并终止它。

通过 read() 无法知道对方是否存活。为什么 SO_KEEPALIVE 有用。 Keepalive 是非侵入性的,在大多数情况下,如果您有疑问,可以将其打开,而不会有做错事的风险。但请记住,它会产生额外的网络流量,这可能会对路由器和防火墙产生影响。

这也会影响您机器上的所有套接字!(您是对的)。并且因为 SO_KEEPALIVE 增加了流量并消耗了 CPU。最好设置 SIGPIPE 句柄,如果应用程序有可能写入断开的连接。

还在应用程序的合理位置使用 SO_KEEPALIVE。在整个连接期间使用它很糟糕(即当服务器在客户端查询上长时间工作时使用 so_keepalive)。

设置探测间隔取决于您的应用程序或说 应用层协议。

虽然启用了 TCP keepalive,但您最终会检测到它 - 至少在几个小时内。

如果网络出现故障,但不是尝试写入,而是将套接字放入某个 epoll 设备中:

epoll中的第二个参数:

 n = epoll_wait (efd, events, MAXEVENTS, -1);

使用正确的事件相关代码设置,好的做法是检查此代码
注意事项如下。

n = epoll_wait (efd, events, MAXEVENTS, -1);  
for (i = 0; i < n; i++)  
{   
    if ((events[i].events & EPOLLERR) ||
          (events[i].events & EPOLLHUP) ||
          (!(events[i].events & EPOLLIN)))
    {
          /* An error has occured on this fd, or the socket is not
             ready for reading (why were we notified then?) */
      fprintf (stderr, "epoll error\n");
      close (events[i].data.fd);
      continue;
    }

    else if (sfd == events[i].data.fd)
    {
          /* We have a notification on the listening socket, which
         means one or more incoming connections. */
         
         // Do what you wants
     }
}

EPOLLRDHUP 的意思是:
Stream socket peer 关闭连接,或者关闭写入一半的连接。 (此标志对于编写简单代码以在使用边缘触发监视时检测对等关闭特别有用。)

【讨论】:

  • SO_KEEPALIVE 默认大约每两小时检查一次连接状态。通常这还不够,它应该作为应用程序协议的一部分来实现。
  • 您还错过了一个无法检测到的错误的原因:端点之间的路由器默默地丢弃了数据包。它甚至可能非常糟糕,它允许 TCP 控制数据包(包括 SO_KEEPALIVE 数据包)通过并且只丢弃实际的数据包。虽然这不太可能在readwrite 上都不会出现问题,但这是实现应用程序级保持活动的另一个原因。
  • @JoachimPileborg:谢谢..编辑了我的答案。
  • @old_bear : SO_KEEPALIVE 消耗 CPU 并增加流量,因此不要使用少于 75 秒的使用时间并处理 SIGPIPE。
  • @old_bear :在需要的代码中也使用保持活动!..reff:编辑答案。
【解决方案2】:

我知道如果 A 上的程序死掉(例如核心转储),这将导致一个 RST 数据包到 B。所以任何 B 的读取尝试都会导致 EOF,而 B 的任何写入尝试都会导致 SIGPIPE .我说的对吗?

部分。 RST 在读取时会导致 ECONNRESET,而不是 EOF,在写入时会导致 EPIPE。

但是,如果假设 A 上的网络出现故障(例如电缆/路由器故障),那么 B 的读/写尝试会发生什么情况?在我的情况下,所有套接字都设置为非阻塞。这样一来,我是不是无法检测到网络错误?

不可能单独读取,除非您使用读取超时,例如通过 select(),并将超时视为失败,这可能不是。在写入时,您最终会得到一个 EPIPE,但由于缓冲和重试,这可能需要一些时间和多次尝试。

【讨论】:

  • 谢谢,写EPIPE平均需要多少时间?
  • @user207421 现在这一分钟还成立吗? (我假设在这方面 TCP 实现相同)
  • @Gukki5 如果 TCP 实现相同,它怎么可能不成立?
猜你喜欢
  • 1970-01-01
  • 2014-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 2020-01-21
  • 1970-01-01
  • 2016-01-31
相关资源
最近更新 更多