> On Wed, Aug 04, 2004 at 04:25:27PM +0200, Michael T Kerrisk wrote:
>
> > But on Linux (2.6), the bind() fails with EADDRINUSE.
>
> This scenario has previously worked for me. Can you show the exact source
> which exhibits this problem?
Okay, sorry for the delay. I had to hack together a simpler
program to demonstrate this. I've attached the program below
(it is both server and client, depending on command line options).
Do note that in my original description, on the first time through,
the server did NOT use SO_REUSEADDR.
In other words, using my program, we do the following:
SERVER HOST CLIENT HOST
./reuseaddr_test -s -n
(The above does NOT use
SO_REUSEADDR)
./reuseaddr_test -c <server-host>
(At this point the server parent exists, but the server
client and child are connected)
./reuseaddr_test -s
(The above DOES use
SO_REUSEADDR)
Now, on Linux, at this point, the second instance of the
server fails with EADDRINUSE, even though it did use
SO_REUSEADDR. On FreeBSD 5.1, the second server instance
does successfully bind.
On Linux, if the *first* instance also used SO_REUSEADDR,
then the second instance can bind. FreeBSD does not require
this.
I realise that the scenario that delivers EADDRINUSE
above is slightly unusual, because normally a server always
specifies SO_REUSEADDR, but my question is why the scenario
I'm describing does differ from FreeBSD, which I'm assuming
conforms to the canonical BSD behaviour.
Cheers,
Michael
==
/* reuseaddr_test.c
Michael Kerrisk, Aug 2004
IPv4 for simplicity...
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define DEF_PORT "55555"
#define DEF_SLEEP_TIME 60
#define errMsg(msg) { perror(msg); }
#define errExit(msg) { perror(msg); exit(EXIT_FAILURE); }
#define fatalErr(msg) { fprintf(stderr, "%s\n", msg); \
exit(EXIT_FAILURE); }
static void
usageError(char *progName, char *msg)
{
if (msg != NULL)
printf("%s", msg);
fprintf(stderr, "Usage: %s [options] -s \n"
" or: %s [options] -c host\n",
progName, progName);
fprintf(stderr, "Options are:\n");
fprintf(stderr, "\t-c host Run as client, connecting to 'host'\n");
fprintf(stderr, "\t-s Run as server\n");
fprintf(stderr, "\t-p port Use given 'port' number (def=%s)\n",
DEF_PORT);
fprintf(stderr, "\t-n *Don't* use SO_REUSEADDR in server\n");
fprintf(stderr, "\t-w nsecs Child sleep interval (def=%d)\n",
DEF_SLEEP_TIME);
exit(EXIT_FAILURE);
} /* usageError */
/* Return Internet address as string: "(host, port#)" */
static char *
inet4AddressString(const struct sockaddr_in *addr)
{
static char buf[1024];
struct hostent *hent;
char *host;
hent = gethostbyaddr((char *) &addr->sin_addr,
sizeof(addr->sin_addr), addr->sin_family);
/* Return host name, or dotted address if not found */
host = (hent != NULL) ? hent->h_name : inet_ntoa(addr->sin_addr);
snprintf(buf, sizeof(buf), "(%s, %u)", host, ntohs(addr->sin_port));
return buf;
} /* inet4AddressString */
int
main(int argc, char *argv[])
{
int opt, sfd, cfd;
int client, server, reuse;
char *pstr = DEF_PORT;
char *host;
struct sockaddr_in svaddr, claddr;
struct hostent *h;
struct in_addr **addrpp;
char buf[1];
socklen_t s;
int optval;
int stime;
#define CMD_SIZE 1024
char cmd[CMD_SIZE];
server = 0;
client = 0;
reuse = 1;
stime = DEF_SLEEP_TIME;
while ((opt = getopt(argc, argv, "c:sp:nw:")) != -1) {
switch (opt) {
case 'c':
client = 1;
host = optarg;
break;
case 's':
server = 1;
break;
case 'p':
pstr = optarg;
break;
case 'w':
stime = atoi(optarg);
break;
case 'n':
reuse = 0;
break;
default:
usageError(argv[0], NULL);
break;
} /* switch */
} /* while */
if ((server && client) || (!server && !client))
usageError(argv[0], NULL);
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1) errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in));
svaddr.sin_family = AF_INET;
svaddr.sin_port = htons(atoi(pstr));
if (server) {
svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
} else { /* client */
h = gethostbyname(host);
if (h == NULL)
fatalErr("host lookup failed (gethostbyname())");
addrpp = (struct in_addr **) h->h_addr_list;
svaddr.sin_addr.s_addr = (*addrpp)->s_addr;
}
if (server) {
if (reuse) {
optval = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) == -1) errExit("setsockopt");
}
if (bind(sfd, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in)) == -1) errExit("bind");
if (listen(sfd, 5) == -1) errExit("listen");
printf("Server listen() completed; about to accept()\n");
s = sizeof(struct sockaddr_in);
cfd = accept(sfd, (struct sockaddr *) &claddr, &s);
if (cfd == -1) errExit("accept");
printf("Server received connection from: %s\n",
inet4AddressString(&claddr));
snprintf(cmd, CMD_SIZE, "netstat -an | grep %s", pstr);
printf("cmd: %s\n", cmd);
//system(cmd);
switch(fork()) {
case -1:
errExit("fork");
case 0:
close(sfd); /* Close listening socket */
sleep(2); /* Give parent a moment to exit */
printf("Server child started\n");
system(cmd);
printf("Server child about to sleep %d seconds\n", stime);
sleep(stime);
printf("Server child exiting\n");
exit(EXIT_FAILURE);
default:
printf("Server parent exiting\n");
exit(EXIT_FAILURE);
}
} else { /* client */
if (connect(sfd, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in)) == -1)
errExit("connect");
printf("Client connected, now about to read\n");
read(sfd, buf, 1);
printf("Client exiting\n");
}
exit(EXIT_SUCCESS);
} /* main */
--
Michael Kerrisk
mtk-lists@xxxxxxx
NEU: WLAN-Router für 0,- EUR* - auch für DSL-Wechsler!
GMX DSL = supergünstig & kabellos http://www.gmx.net/de/go/dsl
|