[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::digital_unix

Title:DIGITAL UNIX(FORMERLY KNOWN AS DEC OSF/1)
Notice:Welcome to the Digital UNIX Conference
Moderator:SMURF::DENHAM
Created:Thu Mar 16 1995
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:10068
Total number of notes:35879

9197.0. "tcp - select() bug in all DUNIX ???" by LEMAN::MARTIN_A (Be vigilant...) Mon Mar 17 1997 03:55

    A customer (ITU) think there is a bug with the TCP and/or the select() 
    system call implementation in the DIGITAL UNIX operating system. 
    
    We tested this on OSF V2.1,  DIGITAL UNIX V3.2C and V4.0A. Very likely 
    all the versions between them have the same problem. We tried it on ULTRIX 
    V4.3, but it does not has the strange behaviour which I'll describe later 
    on. That fact makes me believe that their program isn't faulty (but I'm
    not a tcp/ip programmer expert...).
    
    In .1 you'll find 2 simple C programs that demonstrate the problem, a
    server and a client.
    
    Just compile them with 'make tcpcon' and 'make tcpserv'.
    Run the two programs in two different sessions on the same host:
    in one session type: ./tcpserv 5003
    in the second sesion type: ./tcpcon localhost 5003
    
    The tcpserv opens a socket on the specified port number, listens to
    it, and accepts any incomming connect request. After it shuts down 
    the sending side of the TCP connection. It reads the the connection 
    until EOF, and closes the socket and exits.
    
    The tcpcon makes a conenction to the specified host and port, specified
    as command line arguments. After it shuts down the receiving channel of
    the TCP connection. After that it goes into an infinite loop to write 
    data to this socket, but each write system call is preceded by a select
    system call to be sure that the write call will not block.
    
    The programs are intended to run forever (means transfering data),
    but after a while the sending program hangs on the select system call.
    That is the problem !!!
    
    It looks like that it is a timeing hazard, it does not occur at the
    same amount of transfered data each time the program is run.
    
    We saw that changing the BUFS size does influence the program failure
    behaviour ( in v4.0B with BUFS=1024 it works, but with BUFS=8192 it
    fails too) !!!
    
    Can somebody knowledgeable on TCP/IP programming have a look and tell
    me if it's a programming error or if it's more likely a BUG which
    should be escalated via the IPMT process ?
    
    Is any tcp/ip tuning worth ?
    
    Thanks in advance for your inputs,
    
    					============================
    					Alain MARTIN/SSG Switzerland
    
    
    
    
T.RTitleUserPersonal
Name
DateLines
9197.1tcpserv.c & tcpcon.c source code !LEMAN::MARTIN_ABe vigilant...Mon Mar 17 1997 04:18156
    ************************  tcpserv.c  **********************************
    
    alain.marchi> cat tcpserv.c
    
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
    
    #define BUFS 128
    
    char buf[BUFS];
    
    main(int argc, char * argv[])
      {
      int lsock, sock;
      struct sockaddr_in lsockaddr, peeraddr;
      int peeraddrlen;
      int backlog = 1;
      int remport;
      int l;
    
      if (argc != 2 )
        {
        fprintf(stderr, "Usage: %s port\n", argv[0]);
        exit(1);
        }
      remport = atoi(argv[1]);
      if ( ( lsock = socket(AF_INET, SOCK_STREAM, 0) ) < 0 )
        abort("Socket");
      bzero(&lsockaddr, sizeof(lsockaddr));
      lsockaddr.sin_family = AF_INET;
      lsockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      lsockaddr.sin_port = htons(remport);
      if ( bind(lsock, &lsockaddr, sizeof(lsockaddr)) < 0 )
        abort("Bind");
      if ( listen(lsock, backlog) < 0 )
        abort("Listen");
      if ( ( sock = accept(lsock, &peeraddr, &peeraddrlen) ) < 0 )
        abort("Accept");
      if (close(lsock) < 0)
        abort("Close listen socket");
      if ( shutdown(sock,1) < 0 )
        abort("Shutdown for write");
    
      while ( (l = read(sock,buf,BUFS)) > 0 )
        {
    #if 0
        printf("%d\n", l);
        fflush(stdout);
    #endif
        }
      if ( l < 0 )
          abort("Read");
      if (close(sock) < 0)
        abort("Close peer socket");
      }
    
    abort(char * txt)
      {
      perror(txt);
      exit(1);
      }
    
    
    ***************************  tcpcon.c  ******************************
    
    alain.marchi> cat tcpcon.c
    
    #include <stdio.h>
    #include <ctype.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <time.h>
    
    char * argv0;
    #define BUFS 8192
    char buf[BUFS];
    
    main(argc, argv)
      int argc;
      char * argv [];
      {
      fd_set  writefds;
      int nfds;
      int sockfd;
      struct sockaddr_in serv_addr;
      struct hostent * hstp;
      int openforread;
      int openforwrite;
      char * remhost;
      int s;
    
      argv0 = argv[0];
    
      if ( argc != 3 || ! isdigit(argv[2][0]))
        {
        fprintf ( stderr, "usage: %s host port\n", argv0);
        exit(1);
        }
    
      remhost = argv[1];
    
      if ( ( hstp = gethostbyname(remhost)) == NULL )
        panic("cannot gethostbyname", remhost);
    
      bzero((char *) & serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = *((unsigned long *)*(hstp->h_addr_list));
      serv_addr.sin_port = htons(atoi(argv[2]));
    
      if ( ( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0 )
        abort("Socket");
      if ( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof
    (serv_addr)) < 0 )
        abort("Connect");
      if ( shutdown(sockfd,0) < 0 )
        abort("Shutdown for read");
    
      nfds = getdtablesize();
      if ( nfds > FD_SETSIZE ) nfds = FD_SETSIZE;
      while (1)
        {
        FD_ZERO(&writefds);
        FD_SET(sockfd,&writefds);
        if ( select(nfds, NULL, &writefds, NULL, NULL) != 1 )
          abort("Select");
        if ( write(sockfd, buf, BUFS) != BUFS )
          abort("Write");
        printf("%d\n", BUFS);
        fflush(stdout);
        }
      }
    
    panic(m1, m2)
      char * m1, * m2;
      {
      fprintf(stderr, "%s: %s: %s\n",  argv0, m1, m2);
      exit(1);
      }
    
    abort(char * txt)
      {
      perror(txt);
      exit(1);
      }
    
    
    **************************************************************************
9197.2COL01::LINNARTZMon Mar 17 1997 16:1695
    Alain, as nobody answers, my 2 cent
    
    for me it looks too like a bug. the program hangs in do_scan, but 
    the problem happened in my understanding earlier (soo_select which is
    called by selscan). but it's hard to trace, so I would issue a QAR
    and let some knowledgable look into the flag settings.
    
    a quick and by no mean comprehensive test showed that the poll 
    interface works better, so if possible try some more extensive tests
    and ask the customer if it's possible to replace select by poll
    
    here's you're client example used with poll on sockfd, server unchanged
    #include <stdio.h>
    #include <ctype.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/poll.h>
    #include <netinet/in.h>
    #include <netinet/tcp.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <time.h>
    
    char * argv0;
    #define BUFS 8192
    char buf[BUFS];
    
    main(argc, argv)
      int argc;
      char * argv [];
    {
      int nfds;
      struct pollfd pfd[1];
      int sockfd;
      struct sockaddr_in serv_addr;
      struct hostent * hstp;
      int openforread;
      int openforwrite;
      char * remhost;
      int s;
    
      argv0 = argv[0];
                           
      if ( argc != 3 || ! isdigit(argv[2][0]))
        {
        fprintf ( stderr, "usage: %s host port\n", argv0);
        exit(1);
        }
    
      remhost = argv[1];
    
      if ( ( hstp = gethostbyname(remhost)) == NULL )
        panic("cannot gethostbyname", remhost);
    
      bzero((char *) & serv_addr, sizeof(serv_addr));
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = *((unsigned long *)*(hstp->h_addr_list));
      serv_addr.sin_port = htons(atoi(argv[2]));
    
      if ( ( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) < 0 )
        abort("Socket");
      if ( connect(sockfd, (struct sockaddr *) &serv_addr, sizeof
    (serv_addr)) < 0 )    abort("Connect");
      if ( shutdown(sockfd,0) < 0 )
        abort("Shutdown for read");
    
      nfds = getdtablesize();
      if ( nfds > FD_SETSIZE ) nfds = FD_SETSIZE;
      pfd[0].fd = sockfd;
      pfd[0].events = POLLHUP;
    
      while (1)
        {
    /******
        FD_ZERO(&writefds);
        FD_SET(sockfd,&writefds);
        if ( select(nfds, NULL, &writefds, NULL, NULL) != 1 )
          abort("Select");
    *******/
        if (poll(pfd, 1, -1) < 0)
          abort("Poll");
        if ( write(sockfd, buf, BUFS) != BUFS )
          abort("Write");
        printf("%d\n", BUFS);
        fflush(stdout);
        }
    }
    
    you will see some delay in the client output due to the fact that the
    server reads in the small fragments, but as I understood, thi sis what 
    you wanted to achieve.
    
    Pit  
9197.3a thoughtvaxsim.mro.dec.com::SOUZAFor Internal Use OnlyMon Mar 17 1997 20:197
I wonder if

if ( select(nfds, NULL, &writefds, NULL, NULL) != 1 )

should be

if ( select(nfds+1, NULL, &writefds, NULL, NULL) != 1 )
9197.4VAXCPU::michaudJeff Michaud - ObjectBrokerMon Mar 17 1997 21:5023
> I wonder if
> if ( select(nfds, NULL, &writefds, NULL, NULL) != 1 )
> should be
> if ( select(nfds+1, NULL, &writefds, NULL, NULL) != 1 )

	nope, not in this case.  if they had been using:

		select(socketnumber, ....)

	then it would need the +1, but nfds is correct, and nfds+1
	is incorrect and could result in an EFAULT since the kernel
	may try to read more than the size of fd_set.

	I'm not sure why they aren't using "socketnumber+1" since
	they appear to be trying to optimize in the case where
	the actual size of the fd table is smaller than FD_SETSIZE
	(vs. just using select(FD_SETSIZE, ....))

	note that ideally you should dynamically allocate (ie malloc)
	fd_set based on the number of actual file descriptors because
	it's possible that FD_SETSIZE may be too *small*, especially
	when built on one version of the OS and then run on a latter
	version.
9197.5Fixed by ( HPXQB6189/QAR 49949) patch !!!PANTER::MARTINBe vigilant...Thu Apr 03 1997 07:5820
    Just for closing the loop, the following patch did fix my problem
    with select():
    
    >> MANDATORY patch.
    >>
    >> PROBLEM:  ( HPXQB6189/QAR 49949)         (Patch ID: OSF370-350317)
    >> *******
    >> This patch fixes a network socket problem with select() missing
    >> state changes on clients from non-write to writable.
    >>
    
    There's one for each flavor of OSF from 3.2C to 4.0B !
    
    Thanks to Bob Spear for pointing it out to me !
    
    
    Cheers,
    
    				============================
    				Alain MARTIN/SSG Switzerland