3

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

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

我知道如果 A 上的程序死掉(例如核心转储),那将导致向 B 发送 RST 数据包。因此,对 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 事件吗?

4

2 回答 2

7

TCP 连接还有许多其他方式可以在未被检测到的情况下失效

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

在所有情况下,当您尝试通过程序中的 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 关闭连接,或者关闭写入一半的连接。(此标志对于编写简单代码以在使用边缘触发监视时检测对等关闭特别有用。)

于 2012-10-10T05:54:08.747 回答
4

我知道如果 A 上的程序死掉(例如核心转储),那将导致向 B 发送 RST 数据包。因此,对 B 的任何读取尝试都会导致 EOF,而对 B 的任何写入尝试都会导致 SIGPIPE。我对吗?

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

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

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

于 2012-10-10T03:56:21.760 回答