Linux 套接字编程中接受后的错误号

errno after accept in Linux socket programming

accept() 手册页 RETURN VALUE 部分所述:

Error handling
Linux accept() (and accept4()) passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations. For reliable operation the application should detect the network errors defined for the protocol after accept() and treat them like EAGAIN by retrying. In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH.

这是否意味着必须在 accept() return 之后和检查 accept() 的 return 值之前立即检查 errno 的值?如果是,并且如果设置了 errno,必须采取什么步骤?

这是我的代码处理片段 accept() :

newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if((errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT || errno == EHOSTDOWN ||
    errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENETUNREACH))
    return;
if (newsockfd < 0)
{
    // error
}
else if(newsockfd > 0)
{
    // accepted a new connection
}
else
{
    // blah blah blah
}

我得出的结论是,在这种情况下,您可以稍后再试一次。我的结论正确吗?

根据SUSv4

Upon successful completion, accept() shall return the non-negative file descriptor of the accepted socket. Otherwise, -1 shall be returned and errno set to indicate the error.

这意味着你只需要检查errno if accept() returns -1.

您的代码可能看起来更像这样:

ret = accept(fd, &addr, sizeof (addr));
if (ret == -1) {
    switch (errno) {
    case EAGAIN:
    case EWOULDBLOCK:
        /* do something */
        break;
    case EBADF:
        /* do something different */
        break;
    default:
        /* do something even more different */
    }
}

(具体如何处理每种错误情况取决于您的应用程序。)

此外,重要的是检查 errno 在检查 accept() 的 return 值后立即 。如果您首先调用任何其他函数(即使是简单的 fprintf()),您可能会因不同的错误而覆盖 errno

首先,您检查 accept() return 值。如果 accept() return 值小于 0,那么您应该检查 errno。如果是 ENETDOWNEPROTOENOPROTOOPTEHOSTDOWNENONETEHOSTUNREACHEOPNOTSUPPENETUNREACH,那么您可以再次调用 accept()。否则发生了一些不好的事情,你应该停止调用 accept()(例如,你已经将错误的监听套接字作为 accept() 的参数传递)。

这就是我对代码的理解。

下面是错误处理的方法:

while (running) {
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    if (newsockfd < 0)
    {
        // error
        perror ("accept");
        if((errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT || errno == EHOSTDOWN ||
            errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == ENETUNREACH)) {
            continue;
         }
         exit (EXIT_FAILURE);
    }

    // accepted a new connection
    // blah blah blah
}

您还应该处理 EHOSTUNREACH,它显示 'Software caused connection abort'。

我按照接受的答案处理 accept() return 值,但我的一台服务器因 EHOSTUNREACH 而崩溃。 在 google 之后,我意识到套接字凭据在 ECONNABORTED 错误之后同样没有更改。我们不应该仅仅为了 ECONNABORTED.

让程序崩溃

其各种约束如下:

  1. 如果连接还没有建立,也就是说,如果这是来自客户端的第一个请求,并且如果客户端在服务器有机会响应之前就关闭了连接,接受() 在服务器上调用结果 ECONNABORTED。服务器应忽略此错误并继续处理队列中的下一个请求。如果客户端需要再次连接到服务器,它必须再做一次connect()。

  2. 如果客户端和服务器之间的连接处于 ESTABLISHED 状态,并且如果客户端突然关闭连接,则在服务器端调用 accept() 会导致 ECONNABORTED。在这种情况下,服务器必须关闭半开的连接。否则,只要服务器进程处于活动状态,那些半开的套接字可能会保持 CLOSE_WAIT 状态。请查看此网页:http://technopark02.blogspot.com/200...closewait.html,了解更多关于 CLOSE_WAIT 及其影响的信息,但场景略有不同。

最后完整代码如下:

while(running)
{
    sfd = accept(socketFd, (struct sockaddr *) &cli_addr, &addr_len);
    if( sfd < 0)
    {
        if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENONET ||
            errno == EPROTO || errno == ENOPROTOOPT || errno == EOPNOTSUPP ||
            errno == ENETDOWN || errno == ENETUNREACH || errno == EHOSTDOWN ||
            errno == EHOSTUNREACH || errno == ECONNABORTED)
        {
            log_warn("accept error: %s\n", strerror(errno));
            break;
        }
        else if( errno == EINTR)
        {
            continue;
        }
        else
        {
            log_error("AccepCb: accept error: %s\n", strerror(errno));
            assert(false);
        }
    }

    // logical works...
}