All of lore.kernel.org
 help / color / mirror / Atom feed
* 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.