* Shutdown(), TCP sockets, and select() discrepancy
@ 2002-12-17 17:01 Michael Kerrisk
0 siblings, 0 replies; only message in thread
From: Michael Kerrisk @ 2002-12-17 17:01 UTC (permalink / raw)
To: linux-kernel
After an offline discussion with Andi Kleen, I thought I'd bring this topic
here.
I have been experimenting with shutdown() and select() on TCP sockets and
noted a clear discrepancy from (seemingly all) other Unix flavours. On most
implementations, if we do a SHUT_WR or SHUT_RDWR, then the socket should test
as writable when we call select(), since a write() etc. will fail with a
SIGPIPE signal or an EPIPE error.
On Linux this doesn't happen - is there a reason why Linux differs in this
respect from other implementations? Given that FreeBSD, Solaris 8, HP/UX 11,
Irix 6.5, and OSF 5.1/a all do mark a socket as writable in this case, things
appear to be broken, de facto, on Linux.
At the foot of this mail is a test program I wrote which
demonstrates the disrepancy. When I run this on SuSE 8.0 (kernel 2.4.18)
and SuSE 7.2 (kernel 2.2.19) I see the following:
$ ./is_shutdown_select 1
Listening socket (fd=3) set up okay
Active socket (fd=4) set up okay
Connection established (fd=5)
shutdown(4, 1)
3:
4:
5: r w
$ ./is_shutdown_select 2
Listening socket (fd=3) set up okay
Active socket (fd=4) set up okay
Connection established (fd=5)
shutdown(4, 2)
3:
4: r
5: r w
But On FreeBSD 4.7 I see the following (which is what I expected to see on
Linux):
$ ./is_shutdown_select 1
Listening socket (fd=3) set up okay
Active socket (fd=4) set up okay
Connection established (fd=5)
shutdown(4, 1)
3:
4: w
5: r w
$ ./is_shutdown_select 2
Listening socket (fd=3) set up okay
Active socket (fd=4) set up okay
Connection established (fd=5)
shutdown(4, 2)
3:
4: r w
5: r w
Solaris 8, HP/UX 11, Irix 6.5, and OSF 5.1/a (I had to modify the header
files included slightly for the latter OSes) give the same results as FreeBSD
4.7.
Cheers
Michael
============================
/* is_shutdown_select.c
Experiment to determine behaviour of select() after a shutdown()
is performed on one or both ends of a TCP socket pair.
*/
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#define errExit(msg) { perror(msg); exit(EXIT_FAILURE); }
#define PORT_NUM 50000 /* Port number for server */
int
main(int argc, char *argv[])
{
struct sockaddr_in svaddr;
int fd[3], optval, j;
fd_set rfds, wfds;
int nfds;
struct timeval tmo = { 0, 0 }; /* For polling select() */
if (argc > 1 && strcmp(argv[1], "--help") == 0) {
fprintf(stderr, "%s active-how accept-how\n", argv[0]);
exit(EXIT_FAILURE);
} /* if */
fd[0] = socket(AF_INET, SOCK_STREAM, 0); /* Listening socket */
if (fd[0] == -1) errExit("socket");
optval = 1;
if (setsockopt(fd[0], SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)) == -1) errExit("setsockopt");
/* Bind to wildcard host address + well-known port, and mark as
listening socket */
memset(&svaddr, 0, sizeof(svaddr));
svaddr.sin_family = AF_INET;
svaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Wildcard address */
svaddr.sin_port = htons(PORT_NUM); if (bind(fd[0], (struct sockaddr *)
&svaddr, sizeof(svaddr)) == -1)
errExit("bind");
if (listen(fd[0], 5) == -1) errExit("listen");
printf("Listening socket (fd=%d) set up okay\n", fd[0]);
/* Active sockets */
fd[1] = socket(AF_INET, SOCK_STREAM, 0);
if (fd[1] == -1) errExit("socket");
svaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(fd[1], (struct sockaddr *) &svaddr, sizeof(svaddr)) == -1)
errExit("connect");
printf("Active socket (fd=%d) set up okay\n", fd[1]);
fd[2] = accept(fd[0], NULL, NULL);
if (fd[2] == -1) errExit("accept");
printf("Connection established (fd=%d)\n", fd[2]);
if (argc > 1) { /* Do a shutdown on active socket */
if (shutdown(fd[1], atoi(argv[1])) == -1)
errExit("shutdown (active socket)");
printf("shutdown(%d, %d)\n", fd[1], atoi(argv[1]));
} /* if */
if (argc > 2) { /* Do a shutdown on accepted socket */
if (shutdown(fd[2], atoi(argv[2])) == -1)
errExit("shutdown (accepted socket)");
printf("shutdown(%d, %d)\n", fd[2], atoi(argv[2]));
} /* if */
FD_ZERO(&rfds);
FD_ZERO(&wfds);
nfds = 0;
for (j = 0; j < 3; j++) {
FD_SET(fd[j], &rfds);
FD_SET(fd[j], &wfds);
if (fd[j] >= nfds)
nfds = fd[j] + 1;
} /* for */
if (select(nfds, &rfds, &wfds, NULL, &tmo) == -1) errExit("select");
for (j = 0; j < 3; j++) {
printf("%d: ", fd[j]);
if (FD_ISSET(fd[j], &rfds))
printf("r ");
if (FD_ISSET(fd[j], &wfds))
printf("w ");
printf("\n");
} /* for */
exit(EXIT_SUCCESS);
} /* main */
--
+++ GMX - Mail, Messaging & more http://www.gmx.net +++
NEU: Mit GMX ins Internet. Rund um die Uhr für 1 ct/ Min. surfen!
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2002-12-17 16:53 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2002-12-17 17:01 Shutdown(), TCP sockets, and select() discrepancy Michael Kerrisk
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.