Reputation: 1102
I have a server with a TCP-socket in a non-concurrent single process implementation.
int main(int argc, char** argv) {
int sock_ds, acc_sock_ds, opt, client_addr_l;
unsigned short port;
struct sockaddr_in server_addr, client_addr;
/*Parsing command line: port-number retrieving*/
/*...*/
printf("Port number retrieved (%d), server is starting ...\n", port);
/*TCP Socket creation*/
sock_ds = socket(AF_INET, SOCK_STREAM, 0);
if(sock_ds == -1){
fprintf(stderr, "Socket creation error\n");
exit(EXIT_FAILURE);
}
/*Server address binding*/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
//use setsockopt(2) with OS_REUSEADR ???
if(bind(sock_ds, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
fprintf(stderr, "Address binding error\n");
exit(EXIT_FAILURE);
}
/*Server with passive socket*/
if(listen(sock_ds, SOMAXCONN) == -1){
fprintf(stderr, "Listen call error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("Server is ready. Waiting for connections.\n");
/*Busy-waiting server*/
while(1){
memset(&client_addr, 0, sizeof(client_addr));
acc_sock_ds = accept(sock_ds, &client_addr, &client_addr_l);
/*Connect error management*/
if(acc_sock_ds == -1){
errsv = errno;
if(errsv == 12 || errsv == 23 ){
/*Fatal errors ENOMEM, ENFILE*/
fprintf(stderr, "Fatal error on accept\n");
exit(EXIT_FAILURE);
}
else if(errsv == 103 || errsv == 4 || errsv == 71 || errsv == 92
|| errsv == 112 || errsv == 113 || errsv == 95 || errsv == 101)
/*ECONNABORTED, EINTR, EPROTO, ENOPROTOOPT,
* EHOSTDOWN, EHOSTUNREACH, EOPNOTSUPP, ENETUNREACH*/
continue;
else if(errsv == 100 || errsv == 64){
/* ENETDOWN - ENONET */
/*start timeout...*/
continue;
}
}
}
}
In this test I expected that the server will continue to make the call accept(2)
also on failure. I know there are some terminal error conditions such EBADF
. Should I provide for a different behavior (termination of the process) depending on the value of errno
? For which values the server must be stopped and for which may continue waiting?
Code edited. I never made the error handling via errno. If there are errors or suggestions please notify them. I noticed that the two conditions ENETDOWN
- ENONET
are implied by the absence of network. Should I expect for these situations a timeout to prevent stalling?
Upvotes: 1
Views: 565
Reputation: 44250
Stylistic:
BTW: most of the symbolic constants below don't seem to exist.
#include <errno.h>
#include <strings.h>
/* Busy-waiting server */
while(1){
memset(&client_addr, 0, sizeof(client_addr));
acc_sock_ds = accept(sock_ds, &client_addr, &client_addr_l);
/*Connect error management*/
if(acc_sock_ds == -1){
switch(errsv=errno) {
/*Fatal errors ENOMEN, ENFILE, all others*/
default :
case ENOMEM :
case ENFILE :
fprintf(stderr, "Fatal error on accept %d(%s)\n"
, errsv, strerror(errsv)
);
exit(EXIT_FAILURE);
/* normal NON-ERROR error conditions */
case ECONNABORTED :
case EINTR :
case EPROTO :
case ENOPROTOOPT :
case EHOSTDOWN :
case EHOSTUNREACH :
case EOPNOTSUPP :
case ENETUNREACH :
continue;
case ENETDOWN :
case ENONET :
/*start timeout...*/
continue;
}
}
}
}
Upvotes: 1
Reputation: 339816
Looking at the error codes documented for accept(2)
on MacOS X (and which should be consistent for most any POSIX compliant system):
EBADF
- programmer error - can't happen if the original socket
call succeededECONNABORTED
- try againEFAULT
- programmer error - can't happen unless &client_addr
is invalidEINTR
- call interrupted, try againEINVAL
- "socket is unwilling to accept connections" - I'm not sure how that would happen. It might be due to not calling listen(2)
first (programmer error) although apparently on some OS it can also be caused by the namelen
parameter being negative (also programmer error)EMFILE
- programmer error - you ran out of per-process FDs but that shouldn't happen unless you forgot to close themENFILE
- the system ran out of FDs - this is probably fatalENOMEM
- the system ran out of memory - this is probably fatalENOTSOCK
- programmer error - can't happen unless you passed an FD that wasn't a socketEOPNOTSUPP
- programmer error - can't happen unless you pass a socket that isn't SOCK_STREAM
EWOULDBLOCK
- programmer error - your socket was configured as non-blockingIn addition, it appears that Linux systems can also generate ENETDOWN
, EPROTO
, ENOPROTOOPT
, EHOSTDOWN
, ENONET
, EHOSTUNREACH
, EOPNOTSUPP
, and ENETUNREACH
. These are network errors for which you should loop.
In summary, you should probably:
ENFILE
or ENOMEM
ECONNABORTED
and EINTR
, and the list above from Linuxassert()
during your debugging for anything else - these runtime errors should only happen due to logic errors in the code, and not because of network related events.Upvotes: 2