简体   繁体   中英

errno after accept in Linux socket programming

As stated in accept() man page in RETURN VALUE section:

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 .

Does this mean one must check the value of errno right after the accept() returns and before checking the return value of accept() ? if yes, and if errno is set what steps must be taken?

here's a snippet of my code handling 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
}

I have concluded that in this case one might try again in a while. Is my conclusion correct?

According to 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.

This means that you only need to check errno if accept() returns -1 .

Your code might look more like this:

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

(How exactly you handle each error condition will depend on your application.)

Also, it is important to check errno immediately after checking the return value of accept() . If you call any other functions first (even a simple fprintf() ), you risk overwriting errno with a different error.

First, you check accept() return value. If accept() return value is less than 0 , then you should check errno . If it is ENETDOWN , EPROTO , ENOPROTOOPT , EHOSTDOWN , ENONET , EHOSTUNREACH , EOPNOTSUPP , or ENETUNREACH , then you may call accept() again. Otherwise something bad has happened and you should stop calling accept() (you've passed bad listen socket as accept() 's parameter, for example).

That's how I understand the code.

And here is how the error handling may be done:

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
}

You should handle EHOSTUNREACH as well, which shows 'Software caused connection abort'.

I handled accept() return value as the accepted answer did, but one of my servers crashes because of EHOSTUNREACH . After google, I realize that the socket credentials has not been changed after ECONNABORTED error likewise. We shouldn't crash the program just for ECONNABORTED .

And its various constraints is as follow:

  1. If the connection hasn't been established yet, ie, if it is the very first request from the client and if the client closes the connection even before the server got a chance to respond, accept() call at server results in ECONNABORTED . Server should ignore this error and move on with the next request in the queue. If the client needs to connect to the server again, it has to do another connect().

  2. If the connection between client and server is in ESTABLISHED state, and if the client closes the connection abruptly, accept() call at server results in ECONNABORTED . In this scenario, the server must close the connection that is half-open. Otherwise those half-opened sockets may remain in CLOSE_WAIT state, as long as the server process is active. Have a look at this web page: http://technopark02.blogspot.com/200...closewait.html , to know more about CLOSE_WAIT and its impact, in a slightly different scenario.

Finally, complete code is as follow:

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...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM