When should I use shutdown()?

When should I use shutdown()?

  From Michael Hunter (mphunter@qnx.com):

  shutdown() is useful for deliniating when you are done providing a
  request to a server using TCP.  A typical use is to send a request to
  a server followed by a shutdown().  The server will read your request
  followed by an EOF (read of 0 on most unix implementations).  This
  tells the server that it has your full request.  You then go read
  blocked on the socket.  The server will process your request and send
  the necessary data back to you followed by a close.  When you have
  finished reading all of the response to your request you will read an
  EOF thus signifying that you have the whole response.  It should be
  noted the TTCP (TCP for Transactions -- see R. Steven's home page)
  provides for a better method of tcp transaction management.

  S.Degtyarev (deg@sunsr.inp.nsk.su) wrote a nice in-depth message to me
  about this.  He shows a practical example of using shutdown() to aid
  in synchronization of client processes when one is the "reader"
  process, and the other is the "writer" process.  A portion of his
  message follows:

  Sockets are very similar to pipes in the way they are used for data
  transfer and client/server transactions, but not like pipes they are
  bidirectional.  Programs that use sockets often fork() and each
  process inherits the socket descriptor.  In pipe based programs it is
  strictly recommended to close all the pipe ends that are not used to
  convert the pipe line to one-directional data stream to avoid data
  losses and deadlocks.  With the socket there is no way to allow one
  process only to send data and the other only to receive so you should
  always keep in mind the consequences.

  Generally the difference between close() and shutdown() is: close()
  closes the socket id for the process but the connection is still
  opened if another process shares this socket id.  The connection stays
  opened both for read and write, and sometimes this is very important.
  shutdown() breaks the connection for all processes sharing the socket
  id.  Those who try to read will detect EOF, and those who try to write
  will reseive SIGPIPE, possibly delayed while the kernel socket buffer
  will be filled.  Additionally, shutdown() has a second argument which
  denotes how to close the connection: 0 means to disable further
  reading, 1 to disable writing and 2 disables both.

  The quick example below is a fragment of a very simple client process.
  After establishing the connection with the server it forks.  Then
  child sends the keyboard input to the server until EOF is received and
  the parent receives answers from the server.

       /*
        *      Sample client fragment,
        *      variables declarations and error handling are omitted
        */
               s=connect(...);

               if( fork() ){   /*      The child, it copies its stdin to
                                               the socket              */
                       while( gets(buffer) >0)
                               write(s,buf,strlen(buffer));

                       close(s);
                       exit(0);
                       }

               else {          /* The parent, it receives answers  */
                       while( (l=read(s,buffer,sizeof(buffer)){
                               do_something(l,buffer);

                       /* Connection break from the server is assumed  */
                       /* ATTENTION: deadlock here                     */
                       wait(0); /* Wait for the child to exit          */
                       exit(0);
                       }

  What do we expect? The child detects an EOF from its stdin, it closes
  the socket (assuming connection break) and exits.  The server in its
  turn detects EOF, closes connection and exits.  The parent detects
  EOF, makes the wait() system call and exits.  What do we see instead?
  The socket instance in the parent process is still opened for writing
  and reading, though the parent never writes.  The server never detects
  EOF and waits for more data from the client forever.  The parent never
  sees the connection is closed and hangs forever and the server hangs
  too.  Unexpected deadlock!  ( any deadlock is unexpected though :-)

  You should change the client fragment as follows:

                       if( fork() ) {  /* The child                    */
                               while( gets(buffer) }
                                       write(s,buffer,strlen(buffer));

                                       shutdown(s,1); /* Break the connection
               for writing, The server will detect EOF now. Note: reading from
               the socket is still allowed. The server may send some more data
               after receiving EOF, why not? */
                               exit(0);
                               }

  I hope this rough example explains the troubles you can have with
  client/server syncronization.  Generally you should always remember
  all the instances of the particular socket in all the processes that
  share the socket and close them all at once if you whish to use
  close() or use shutdown() in one process to break the connection.



Home
FAQ