This is the mail archive of the
cygwin
mailing list for the Cygwin project.
Issue with socket SO_REUSEADDR when a client is connected.
- From: jdzstz - gmail dot com <jdzstz at gmail dot com>
- To: cygwin at cygwin dot com
- Date: Sun, 30 Jan 2011 15:31:44 +0100
- Subject: 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