Reputation: 2657
As stated in accept()
man page in RETURN VALUE
section:
Error handling
Linuxaccept()
(andaccept4()
) passes already-pending network errors on the new socket as an error code fromaccept()
. This behavior differs from other BSD socket implementations. For reliable operation the application should detect the network errors defined for the protocol afteraccept()
and treat them likeEAGAIN
by retrying. In the case of TCP/IP, these areENETDOWN
,EPROTO
,ENOPROTOOPT
,EHOSTDOWN
,ENONET
,EHOSTUNREACH
,EOPNOTSUPP
, andENETUNREACH
.
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?
Upvotes: 4
Views: 9971
Reputation: 55
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:
If the connection hasn't been established yet, i.e., 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().
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...
}
Upvotes: 0
Reputation: 2753
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
}
Upvotes: 7
Reputation: 106
According to SUSv4:
Upon successful completion,
accept()
shall return the non-negative file descriptor of the accepted socket. Otherwise,-1
shall be returned anderrno
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.
Upvotes: 3