=========================== EOF handling between client and server =========================== -Ian! D. Allen - idallen@idallen.ca - www.idallen.com Make sure your client and server handle EOF or the disappearance of the other end of the connection (process termination) correctly. 1. Client gets EOF from Standard Input -------------------------------------- A typical client has two processes (or perhaps two threads) - one looping reading Standard Input (often the keyboard) and writing the connected server socket, the other looping reading the connected server socket and writing to Standard Output (usually the screen). Let's call the first one (reading the keyboard) the client Stdin (stdin-to-server) process and the second one (writing the screen) the client Stdout (server-to-stdout) process. On getting EOF (or error) from the keyboard (usually triggerd by ^D), the client Stdin process, before it exits, needs to tell the server that there is nothing more coming down the socket. The client has to signal "EOF" to the server. To do this, the client must use shutdown(fd,SHUT_WR) on the connected server socket before it exits. This shutdown() will close only the writing (not reading) half of the socket. (Don't close the reading half yet! Close only the client writing direction of the socket; make sure the argument to shutdown is 1 or SHUT_WR.) The client Stdout process will continue to run, still reading from non-closed reading half of the server socket. (Only the writing direction of the socket should be shut down using shutdown(fd,SHUT_WR).) After the client Stdin process does shutdown(fd,SHUT_WR), the server will see the EOF on its matching reading half of the client socket, and the server will fully close() the socket at its end. Before the server closes the socket, it might wish to write a "goodbye" message to the client. After the server fully closes its end of the socket, the client Stdout process (reading the server socket) will now see EOF on the reading half of the server socket. The client Stdout process can then print a "goodbye" message and exit. Both client processes have now exited. The sequence is: 1. client Stdin process sees EOF or error on standard input and calls shutdown(fd,SHUT_WR) on the server socket to send "EOF" to the server 2. client Stdin process exits, since there is no more data incoming 3. server child sees EOF from client and calls close() on client socket 4. server child exits, since EOF means the client has no more data 5. client Stdout process now sees EOF when reading the server socket 6. client Stdout exits, since there is nothing more to read from the server 2. Client gets EOF from Server ------------------------------ If the server process dies or exits, the client Stdout process (server-to-stdout) will be the first one to know, by seeing EOF on the server socket it is reading. If this client Stdout process simply exits, it leaves the client Stdin process hung still reading the user's keyboard, even though there is no place to send the keyboard data; because, the server is dead. (Note: A write to a closed socket will cause your process to receive a SIGPIPE signal and die - see "man 7 socket" under the heading "SIGNALS".) The only solution for a server EOF is for the client Stdout process to know the process ID of the client Stdin process, and the Stdout process must send a SIGHUP signal to the Stdin process before it exits. This will kill the client process hung reading the keyboard, and thus both child processes will be gone and the keyboard will be freed. So, just before the Stdout process exits, it must send SIGHUP to the Stdin process, to free up the keyboard. EOF WARNING for coding forking servers: In a server that forks to handle clients, some things can prevent EOF from being seen by a client even though a server process calls close() on a client socket. When the server forks after an accept(), both processes (parent and child) have a copy of the open client socket file descriptor. For EOF to be seen by the client on the socket, *both* processes have to close the client socket descriptor. The child process forked to handle the client will usually be the one reading/writing the socket; so, the parent *must* close its duplicate copy of the client socket descriptor. If the parent keeps an open copy of the file descriptor, then even if the child closes its copy of the descriptor the remote client won't see an EOF because the parent still has the descriptor open. In a server that forks children to handle client connections, make sure the parent closes its duplicate copy of the client socket descriptor after the fork(). The forked child process of the server then can call close() and the client will correctly see EOF.