This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Issue with socket SO_REUSEADDR when a client is connected.


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]