簡體   English   中英

在Linux套接字編程中接受后的錯誤

[英]errno after accept in Linux socket programming

RETURN VALUE部分中的accept()手冊頁所述:

錯誤處理
Linux accept() (和accept4() )將新套接字上已經掛起的網絡錯誤作為來自accept()的錯誤代碼傳遞。 此行為與其他BSD套接字實現不同。 為了可靠運行,應用程序應在accept()之后檢測為協議定義的網絡錯誤,並通過重試將其視為EAGAIN 在TCP / IP的情況下,這些都是ENETDOWNEPROTOENOPROTOOPTEHOSTDOWNENONETEHOSTUNREACHEOPNOTSUPP ,以及ENETUNREACH

這是否意味着必須在accept()返回之后和檢查accept()的返回值之前檢查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

成功完成后, accept()將返回已接受套接字的非負文件描述符。 否則,返回-1errno以指示錯誤。

這意味着如果accept()返回-1 ,您只需要檢查errno

您的代碼可能看起來更像這樣:

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 */
    }
}

(您如何處理每個錯誤情況取決於您的應用程序。)

另外,在檢查accept()的返回值后立即檢查errno很重要。 如果你先調用任何其他函數(即使是一個簡單的fprintf() ),你也有可能用不同的錯誤覆蓋errno

首先,檢查accept()返回值。 如果accept()返回值小於0 ,那么你應該檢查errno 如果是ENETDOWNEPROTOENOPROTOOPTEHOSTDOWNENONETEHOSTUNREACHEOPNOTSUPP ,或ENETUNREACH ,那么你可以調用accept()一次。 否則發生了一些不好的事情,你應該停止調用accept() (例如,你已經通過bad listen socket作為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 ,它顯示“軟件導致連接中止”。

我處理了accept()返回值作為接受的答案,但是我的一台服務器由於EHOSTUNREACH崩潰。 在google之后,我意識到在ECONNABORTED錯誤之后同樣沒有更改套接字憑據。 我們不應該只為ECONNABORTED崩潰程序。

其各種約束如下:

  1. 如果尚未建立連接,即,如果它是來自客戶端的第一個請求,並且如果客戶端甚至在服務器有機會響應之前關閉連接,則服務器上的accept()調用結果為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...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM