I have detected an issue in windows xp with socket SO_REUSEADDR when a tcp client keeps connected forever.
I don't know if it is a cygwin bug or a problem with microsoft winsock implementation. I don't know if windows vista or windows 7 has the same behaviour. I have a server process that open a socket and binds to a port. After a while, it closes the socket, re-open and re-bind to it. In Unix (linux, solaris) the server process can always re-bind after closing the previous socket, but in cygwin, there are some strange issues. I have prepared a simplified tcp_server test program, in order to be able to reproduce error in an easy way. A) If I bind to wildcard address 0.0.0.0 port 5555, it works successfully, like Unix: 1) tcp_server binds to 0.0.0.0:5555 and create a thread to accept connections 2) tcp_server sleep for 10 seconds 3) a client process connect to 127.0.0.1:5555 and keeps connected forever 4) tcp_server awakes, closes all connections to port 5555 (but client process keeps port connected, in ESTABLISHED state) 5) tcp_server successfully reopens and rebind to 0.0.0.0:5555 B) If I bind to address 127.0.0.1 port 5555, it fails: 1) tcp_server binds to 127.0.0.1:5555 and create a thread to accept connections 2) tcp_server sleep for 10 seconds 3) a client process connect to 127.0.0.1:5555 and keeps connected forever 4) tcp_server awakes, closes all connections to port 5555 (but client process keeps port connected, in ESTABLISHED state) 5) an error "bind(): Address already in use" occurs. C) If there is no client connection, rebind is always successful. TEST LOG: ========= A) bind to 0.0.0.0 5555 $ ./tcp_server 0.0.0.0 5555 10 Start main Opening socket on 0.0.0.0 5555 Main: Creating thread Start sleep Starting server thread, executing "accept" accepted fd 4 End sleep pthread_cancel Waiting for server Server returned "0xffffffff" End main Start main Opening socket on 0.0.0.0 5555 Main: Creating thread Start sleep Starting server thread, executing "accept" End sleep pthread_cancel Waiting for server Server returned "0xffffffff" End main Start main Opening socket on 0.0.0.0 5555 B) bind to 127.0.0.1 5555 $ ./tcp_server 127.0.0.1 5555 10 Start main Opening socket on 127.0.0.1 5555 Main: Creating thread Starting server thread, executing "accept" Start sleep accepted fd 4 End sleep pthread_cancel Waiting for server Server returned "0xffffffff" End main Start main bind(): Address already in use Main: Creating thread Start sleep Starting server thread, executing "accept" Accepted failed: Bad file descriptor Ending thread End sleep pthread_cancel Waiting for server close = 9 Bad file descriptor End main Start main bind(): Address already in use I attach to email a tcp_server and tcp_client examples. Execute "./tcp_server 127.0.0.1 5555 10" in one terminal and "./tcp_client 127.0.0.1 5555" in other. A telnet connection to tcp_server is also OK to see the problems. This problem occurs sometimes in "varnishtest" tool from varnish cache software.
#include <errno.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <netdb.h> #include <poll.h> char* host; int port; int seconds; int sock; /********************************************************************** * Listen to port */ int listen_port() { int i, ret; int sd, val; sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { perror("socket()"); return (-1); } val = 1; if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val) != 0) { perror("setsockopt(SO_REUSEADDR, 1)"); (void)close(sd); return (-1); } struct hostent *ptrh; struct sockaddr_in saddr; ptrh = gethostbyname(host); if ( ((char *)ptrh) == NULL ) { perror("Invalid host"); return (-1); } bzero((char *) &saddr, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons((u_short)port); memcpy(&saddr.sin_addr, ptrh->h_addr, ptrh->h_length); if (bind(sd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) { perror("bind()"); (void)close(sd); return (-1); } return (sd); } /********************************************************************** * Server Thread */ static void * server_thread(void *priv) { struct vtclog *vl; int i, j, fd; struct sockaddr_storage addr_s; struct sockaddr *addr; socklen_t l; char buf[1000]; /* buffer for string the server sends */ printf("Starting server thread, executing \"accept\"\n"); addr = (void*)&addr_s; l = sizeof addr_s; fd = accept(sock, addr, &l); if (fd < 0) { printf( "Accepted failed: %s\n", strerror(errno)); } else { printf( "accepted fd %d\n", fd); int n; n = recv(fd, buf, sizeof(buf), 0); while (n > 0) { send(fd, buf, n, 0); n = recv(fd, buf, sizeof(buf), 0); } printf( "shutting fd %d\n", fd); j = shutdown(fd, SHUT_WR); if (!((j) == 0 || errno == ECONNRESET || errno == ENOTCONN)) printf( "Shutdown failed: %s\n", strerror(errno)); i = close(fd); printf( "closed fd %d\n", fd); assert (((i) == 0 || errno == ECONNRESET || errno == ENOTCONN)); } printf("Ending thread\n"); return (NULL); } /********************************************************************** * Main */ int main(int argc, char *argv[]) { if(argc!=4) { printf("%s <address> <port> <seconds>\n",argv[0]); return; } else { host = argv[1]; port=atoi(argv[2]); seconds=atoi(argv[3]); } while(1) { printf( "Start main\n"); sock = listen_port(); if (sock >= 0) { if (listen(sock, 1) != 0) { perror("listen()"); (void)close(sock); return (-1); } else { struct sockaddr_storage addr_s; socklen_t l; l = sizeof addr_s; getsockname(sock, (void *)&addr_s, &l); int i; char aaddr[32]; char aport[32]; i = getnameinfo((const void *)&addr_s, l, aaddr, sizeof aaddr, aport, sizeof aport,NI_NUMERICHOST | NI_NUMERICSERV); if (i) { printf("getnameinfo = %d %s\n", i, gai_strerror(i)); return; } printf( "Opening socket on %s %s\n", aaddr, aport); } } pthread_t thr; int rc; long t; printf("Main: Creating thread\n", t); rc = pthread_create(&thr, NULL, server_thread, (void *)t); if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } printf("Start sleep\n"); sleep(seconds); printf("End sleep\n"); printf("pthread_cancel\n"); (void)pthread_cancel(thr); void *res; printf("Waiting for server\n"); rc=pthread_join(thr, &res); if (rc) { printf("ERROR; return code from pthread_join() is %d\n", rc); } if (res != NULL) printf( "Server returned \"%p\"\n",(char *)res); int j; j = close(sock); if (j) { printf("close = %d %s\n", errno, strerror(errno)); } printf("End main\n"); } return; }
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <string.h> int main(int argc, char * argv[]) { struct hostent *ptrh; /* pointer to a host table entry */ struct protoent *ptrp; /* pointer to a protocol table entry */ struct sockaddr_in sad; /* structure to hold an IP address */ int sd; /* socket descriptor */ int port; /* protocol port number */ char *host; /* pointer to host name */ int n; /* number of characters read */ char buf[1000]; /* buffer for data from the server */ char *text; /* pointer to user's line of text */ memset((char *)&sad,0,sizeof(sad)); /* clear sockaddr structure */ sad.sin_family = AF_INET; /* set family to Internet */ if(argc!=3) { printf("%s <address> <port>\n",argv[0]); return; } else { host = argv[1]; port = atoi(argv[2]); } if (port > 0) /* test for legal value */ sad.sin_port = htons((u_short)port); else { /* print error message and exit */ fprintf(stderr,"Bad port number %s\n",argv[2]); exit(1); } /* Convert host name to equivalent IP address and copy to sad. */ ptrh = gethostbyname(host); if ( ((char *)ptrh) == NULL ) { fprintf(stderr,"Invalid host: %s\n", host); exit(1); } memcpy(&sad.sin_addr, ptrh->h_addr, ptrh->h_length); /* Map TCP transport protocol name to protocol number. */ if ( ((int)(ptrp = getprotobyname("tcp"))) == 0) { fprintf(stderr, "Cannot map \"tcp\" to protocol number"); exit(1); } /* Create a socket. */ sd = socket(AF_INET, SOCK_STREAM, ptrp->p_proto); if (sd < 0) { fprintf(stderr, "Socket creation failed\n"); exit(1); } /* Connect the socket to the specified server. */ if (connect(sd, (struct sockaddr *)&sad, sizeof(sad)) < 0) { fprintf(stderr,"Connect failed\n"); exit(1); } /* Repeatedly read data from user and send it to server. */ text = fgets(buf, sizeof(buf), stdin); while (text != NULL) { send(sd, buf, strlen(buf), 0); n = recv(sd, buf, sizeof(buf), 0); write(1, buf, n); text = fgets(buf, sizeof(buf), stdin); } /* Close the socket. */ close(sd); /* Terminate the client program gracefully. */ exit(0); }
-- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple