Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition)
14.8 Sockets and Standard I/O
In all our examples so far, we have used what is sometimes called Unix I/O , the read and write functions and their variants ( recv , send , etc.). These functions work with descriptors and are normally implemented as system calls within the Unix kernel. Another method of performing I/O is the standard I/O library . It is specified by the ANSI C standard and is intended to be portable to non-Unix systems that support ANSI C. The standard I/O library handles some of the details that we must worry about ourselves when using the Unix I/O functions, such as automatically buffering the input and output streams. Unfortunately, its handling of a stream's buffering can present a new set of problems we must worry about. Chapter 5 of APUE covers the standard I/O library in detail, and [Plauger 1992] presents and discusses a complete implementation of the standard I/O library. The term stream is used with the standard I/O library, as in "we open an input stream" or "we flush the output stream." Do not confuse this with the STREAMS subsystem, which we will discuss in Chapter 31. The standard I/O library can be used with sockets, but there are a few items to consider:
Example: str_echo Function Using Standard I/O
We now show an alternate version of our TCP echo server (Figure 5.3), which uses standard I/O instead of read and writen . Figure 14.14 is a version of our str_echo function that uses standard I/O. (This version has a problem that we will describe shortly.) Figure 14.14 str_echo function recoded to use standard I/O.
advio/str_echo_stdio02.c 1 #include "unp.h" 2 void 3 str_echo(int sockfd) 4 { 5 char line[MAXLINE]; 6 FILE *fpin, *fpout; 7 fpin = Fdopen(sockfd, "r"); 8 fpout = Fdopen(sockfd, "w"); 9 while (Fgets(line, MAXLINE, fpin) != NULL) 10 Fputs(line, fpout); 11 } Convert descriptor into input stream and output stream
7 “10 Two standard I/O streams are created by fdopen: one for input and one for output. The calls to read and writen are replaced with calls to fgets and fputs . If we run our server with this version of str_echo and then run our client, we see the following:
There is a buffering problem here because nothing is echoed by the server until we enter our EOF character. The following steps take place:
The problem here is the buffering performed automatically by the standard I/O library on the server. There are three types of buffering performed by the standard I/O library:
Most Unix implementations of the standard I/O library use the following rules:
Since a socket is not a terminal device, the problem seen with our str_echo function in Figure 14.14 is that the output stream ( fpout ) is fully buffered. One way around this is to force the output stream to be line buffered by calling setvbuf . Another is to force each echoed line to be output by calling fflush after each call to fputs . But in practice, either of these solutions is still error-prone and may interact badly with the Nagle algorithm described in Section 7.9. In most cases, the best solution is to avoid using the standard I/O library altogether for sockets and operate on buffers instead of lines, as described in Section 3.9. Using standard I/O on sockets may make sense when the convenience of standard I/O streams outweighs the concerns about bugs due to buffering, but these are rare cases. Be aware that some implementations of the standard I/O library still have a problem with descriptors greater than 255. This can be a problem with network servers that handle lots of descriptors. Check the definition of the FILE structure in your <stdio.h> header to see what type of variable holds the descriptor. |