3. 11 Summary

It is possible for a programmer to create network applications that operate across the global Internet without understanding how networks operate or how the underlying technologies carry the data between computers. The programmer must be given a set of high-level functions that form an Application program Interface (API). This chapter presented a network ApI that contains only seven primitives, and reviewed example applications that show the API is sufficient to construct software that corTectly interoperates with commercial software.

3. 1 The echo service is a standasigned application number 7. see if they run a standard echo server.
3. 2 Modify the echo server so that instead of exiting after it handles one client, the server waits for another client. Hint : look at the web server.
3. 3 0ur chat software requires the users to take turns entering text. Rewrite the software to allow either user to type an arbitrary number of lines at any time. Hint : use threads.
3. 4 Modify the chat client to send a user name with each message, and modify the server to identify a user when displaying a line of output.
3. 5 Extend the above exercise so that instead of sending the user name with each message, the chat client and server exchange user names when they first make contact.
3. 6 Why does the example code use a mixture of calls to write and various forms of prinrf? Hint : does Windows treat sockets, files, and pipes identically?
3. 7 Devise software that permits an n-way chat session that allows users to join and leave the session at any time.
3. 8 Try the web client program with well-known lnternet web servers. To do so, give the server’s name, a path of index. Html or index. Htm, and application number 80.
3. 9 Add another”page”to the web server.
3. 10 Modify the web server so it extracts the contents of each page from a file instead of having them hard-wired into the code.
3. 11 Expand the previous exercise to recognize file names that end in. Gif and send them using a Colztent-i)pe header with a value imageTgif instead of the string textlJztml.
3. 12 (advanced) Implement the Common Gateway Interface (CGI) from the specification found at http :// hoohoo. ncsa. uiuc. edu/cgi/
3. 13 (advanced) Extend the web server so it can handle multiple connections concurTently. Hint usefork or ptbread_create. ird service available throughout the Internet. It has been as Use the echo client to test computers in your organization to

3. 10 Code For A Web Application

The final example application we will consider consists of a client and server for the World Wide Web. To run the server, a user chooses an application number and invokes the server program. for example, if a user on computer mertin. cs. purdue. edu chooses application number 27ooo, the server can be invoked with the command webserver 27000
As usual, the client specifies a computer, a path name, and an application number webclient merlin. cs. purdue. edu / index. html 27000 although extremely small, our web server follows the standard protocols. thus, it is possible to use a conventional (I. e., commercially available) web browser to access
the server. For example, to use a commercial browser instead of our webclient in the example above, one enters the URL
http :// merlin. cs. purdue. edu : 27000/index. html
To keep our code a short as possible, we make a few simplifying assumptions. For example, the server only supplies three web pages, and none of the pages contains anything except text. Furthermore, each page is hard-wired into the code ; the page can only be changed by recompiling the servert.
The most significant limitation of our web application lies in the client. Unlike a conventional web browser, our client code does not understand how to format and display web pages. Instead, the client merely prints the source of the page. Despite the limitation, the client does interoperate with a commercial web server-it can be used to print the source of any page available on the World Wide Web.

3. 10. 1 Example Web Client Code
File webclient. c contains the code for the web client.
/ * webclient. c */


#de£ine BUFFSIZE 256

* Program webc1 ient

* purpose fetch page from webserver and dump to stdout with headers
* Usage webclient 4)
(void) fprintf (stderr,”%s%s%s”, USage”, argv [ 0 ]
(void) write (STDOUT_FILEI¢O, buff, len)
return o
The client code is extremely simple-after establishing communication with the web server, it sends a request, which must have the formt
wr / path/1. 0 CRLF CRLF where path denotes the n£ane of an item such as index. Html, and CRLF denotes the two characters carriage return and line feed. After sending the request, the client receives and prints output from the server.

3. 1o. 2 Example web Server Code

File webserver. c contains the code for a (miniature) web server.

/ * w € bserver. c k /



#if defined (LINUX)

#include «sys/time. H>

defined (SOmis)

#define BUFFSIZE


#define ERROL4 0 0″«head>«body>«Dm» «nJ 7o_'”—
e server couldn’t understand your request. \ n”
#define EBRoL404″Er ror 404«/hl»«P»Do \
cument not found.
\ n”
#ciefine HOT¢E_pAGE”«hl>Welc ome to the oqAl \

“CNAI Demo Web Server”
Sec. 3. 10 Code For A Web Application

rdue. edul · >netbook home page

  • \ n”

    int recvln{connection, char *, int)
    void send head{connection, int, int)
    * Program webserver
    * puri»ose serve hard-coded webpages to web clients
    * Usage webserver \n”, argv [ 0 ] )
    exit (1)

    while (1)
    / * wait for contact £rom a client on specified appnum */
    conn = await_contact ( (appnum) atoi (argv[1] ) )
    i£ (conn < O) exit (1) conn n buff [BUETSIZE], cmd[16], path[64], vers [16] *timestr defined (SOLARIS) Lr 38 Network Programming And Applications Chap. 3 / * read and parse the request line */ n-recvln (conn, buff, BUFFSIZE) sscanf (buf f, I : %s %s %s", cmd, path, vers) / * skip all headers-read until we get \ r \ n alone * / 1.. Ihile((n-recvln(conn, bu£f, BUFFSIZE) ) > O)

    if (n == 2 & & buff [0] ==’\ r’& & bu£f [1] ==’\ n’)

    / * check for unexpected end of file * /
    if (n < 1) (void) send eof (conn) continue / * check for a request that we cannot lmderstand */ if (strcmp(cmd,"GET") (strarw(vers,"rqTTPH. O") & & streng) (vers,"HTTP/1. 1") ) ) send head(conn, 400, strlen (EBROL400 ) ) (void) send(conn, ERROL400, strlen (ERROR400), O) (void) send eof (conn) continue / * send the requested web page or a"not £ouncL''error */ if (strulu(path,"/") == O) send head(conn, 200, strlen (HOME_PAGE) ) (voici) send (conn, HOME_PAGE, strlen (HOME_PAGE), O) else if (strcmp (path,"/ time") == O) #if defined (LINUX) I defined (SOLARIS) gettimeofw(&tv, NULL) timestr-ctime (& tv. Tv_sec) #endif timestr-ctime (& tv) (void) sprint£ (buf £, TWPAGE, tLmestr) senMead(conn, 200, strlen (buff) ) Sec. 3. 10 Code For A Web Application 39 (void) send (conn, buf f, strlen (buf f), O) else { / * not found * / senLhead(conn, 404, strlen (ERROL404) ) (void) send (conn, EnRoL40a, strlen (ERROL404), O) * send head-send an HTTp 1. o header with given status and content-len void send head (connection conn, int stat, int len) char *gtatstr, buff [BUFFSIZE] / * convert the status code to a string */ switch (stat) case 200 statstr ="OK" breaK case 4001 statstr ="Bad Request" break case 404 statstr ="Not Found n break default statstr ="own" break * send an HTTP/1. 0 response with Server, COntent-Length, * and Content-Type headers. (void) sprintf (buff,"HTTP/1. 0 %d %s \ r \ n", stat, statstr) (void) send (conn, buff, strlen (buf£), O) (void) sprintf (buff,"Server %s\r\n", SERI 40 Ld Network Programming And Applications Chap. 3 (void) send (conn, buff, strlen (buff), O) (void) SPrint£ (buff,"Content-Length %d\r\n", leni (void) send (conn, buff, strlen (buff), O) (void) sprintf (buff,"content-Type text/htm1\r\n") (void) send (conn, buff, strlen (buff), O) (void) sprintf (buff,"\ r \ n") (void) send (conn, buff, strlen (buff), O ) Although the web server may seem more complex than previous examples, most of the complexity results from Web details rather than networking details. In addition to reading and parsing a request, the server must send both a"header"and data in the response. The header consists of several lines of text that are terminated by the caiTiage return and linefeed characters. The header lines are of the form WP? / 1. 0 status status_string CRLF Server oml Demo CRLF Ccmtent-LiEmgtin datasize CRLF C«mtent-W teDct/htm1 CRLF CRLF where datasize denotes the size of the data that follows measured in bytes. Procedure seizd_lzead handles the chore of generating a header. WDen send_head is called, argument stat contains an integer status code and argument len specifies the content length. The switch statement uses the code to choose an appropriate text message, which is assigned to variable statstr. Send_lzead uses the C function sprin(T to generate the complete header in a buffer, and then calls send to transmit the header lines over the connection to the client. The code is also complicated by etTor handling-error messages must be sent in a form that a browser can understand. If a request is incorTectly formed, our server generates a 400 error message ; if the item specified in the request cannot be found (I. e., the parA is incorrect), the server generates a 404 message. Our web server does differs from the previous examples in a significant way : the server program does not exit after satisfying one request. Instead, the server remains running, ready to accept additional requests. That is, the server program consists of an infinite loop that calls await_contact to wait for contact from a client. When contact arrives, the server calls recvln to receive a request and calls send to send a response. The server then goes back to the top of the loop to wait for the next contact. Thus, once it is started, the server runs forever, just like a commercial web server.

  • 3. 9 Code For A Chat Application

    The second application we will consider is a simplified form of the clzat facility. On the Internet, chat allows a group of users to communicate by entering text messages that are displayed on each others’screens. Our software provides a simplified version of chat that works between a single pair of users-when one user enters text, the text is displayed on the other user’s screen, and vice versa. Furthermore, like the echo application described earlier, our chat software can be used between any computers connected to the Internet. One user begins by choosing an application number and running the server. For example, suppose a user on computer excalibur. cs. purdue. Edu runs the A user on another computer can invoke the client, which contacts the server chatclient excalibur. cs. purdue. Edu 25000 To keep the code as small as possible, we have chosen a scheme that requires users to take turns entering text. Both the client and server issue a prompt when the user on that side is expected to enter a line of text. The user on the client side is prompted for input first. When a line of text has been received, the client sends the line to the server and the roles reverse. Users alternate entering text until one of them sends an end-of-file.

    The code itself is straightforward. The server begins by waiting for contact from the client. It then enters a loop in which it obtains and displays a line of text from the client, prompts the local user, reads a line of input from the keyboard, and sends the line to the client side. Thus, until it receives an end-of-file, the server iterates between displaying output *om the client and sending keyboard input to the client.

    The client begins by contacting the server. Once communication has been established, the client also enters a loop. During each iteration, the client prompts the local user to enter a line of text, reads a line from the keyboard, sends the line to the server, and then receives and displays a line of text from the server. Thus, the client continues to alternate between sending a line of text that the user enters and displaying a line of text from the server.

    3. 9. 1 Example Chat Server Code
    File clzatserver. c contains the code for the chat server.

    / *-chatserver. c : */



    #define BUFFSIZE 2 5 6

    #de£ine INPUT_PROD®T”Input >
    #define RECEHIED_pRODIpT”Received»”

    int recvln(connection, char *, int)
    int readln(char *, int)

    Program chatserver

    * purpose wait for a connection from a chatclient & allow users to chat
    * Usage chatserver


    maintint arge, char *argvt] )

    connection conn
    int len

    char buff [BUFFSIZE]

    if (arge != 2 )

    (void) fprintf(stderr,”usage %s \n”, argv[o] )
    exit (1 )

    (void) print£ (“Chat Server Waiting For Connection. \ n”)
    / * wait for a connection from a chatclient */
    conn-await_contact ( (appnum) atoi (argv[1] ) )
    i£ (conn < O ) exit (1) (void) printf ("Chat Connection Established. \ n") / * iterate, reading £rom the client and the local user * / whi le((1en-recvln(conn, bu£f, BUFFSIZE) ) > O)

    (void) printf (RECEIVED_PROMPT)
    (void) fflush (stdout)
    (void) write (SmouT_FILENO, buf£, len)

    / * send a line to the chatclient * /
    (void) printf (INP【JT_PROMPT)
    (void) fflush (stdout)

    if ( (len-readln(buf £, BUFFSIZE) ) < 1) break buf£[1en-1] ='\ n' (void) send (conn, buf£, len, O) / * iteration ends \nihen EOF found on stdin or chat connection * / (void) send eof (conn) (void) printf ("\ nchat Connection Closed. \ n \ n") retun o Functions, recvln and readln, simplify the code-they each consist of a loop that iterates until an entire line or end-o6file is encountered. Recvln calls recv to receive from a network connection, and readln calls read to read characters from a keyboard. The overall structure of the chat server is similar to the echo server we examined earlier. Like the echo server, the chat server expects a single command-line argument that is the application number to use. Once contact arTives from a client, the chat server prints a message for the local user, and enters a loop. At each iteration, the server receives a line of text from the network connection, prints the line on the user's screen, reads a line of input from the keyboard, and sends the line over the network. When it detects an end-of-file, the server sends an end-of-file and exits. 3. 9. 2 Example Chat Client Code File clzatclient. c contains the code for the chat client. / * chatclient. c */ #include


    #define BUFFSIZE 256

    #define INPUT_PROm”Input >

    #define RECEDM_PROMPT”Received»”

    int recvln (connection, char *, int)
    int readln (char *, int)
    * Program chatclient

    * purpose contact a chatserver and allow users to chat

    * Usage chatclient O )

    buff ilen-1] = \ n’
    (void) send (conn, buff, len, O)

    / * receive and print a line frem the chatserver * /
    if ( (len-recvln(conn, buf£, BUFFSIZE) ) < 1) break (void) printf (RECEDED_PROMPT) (void) fflush (stdout) (void) write (STDO【JT_FILEUO, buf f, len) (void) printf (INPUT_PROMPT) (void) fflush (stdout) / * iteration ends when stdin or the connection indicates EOF * / (void) printf ("\ nchat Connection Closed. \ n") (void) send eof (conn) exit (0) The client begins by contacting a server. Once communication has been established, the client enters a loop that reads from the keyboard, sends the data to the server, receives a line from the server, and displays the line on the user's screen. The iteration continues until the client receives an end-of-file condition from the server or an end-of-file from the keyboard (a return value of zero). At that time, the client sends an end-oFfile and exits.

    3. 8 Code For An Echo Application

    The first application we will consider is trivial : the server merely echoes back all the data it receives. The client repeatedly prompts the user for a line of input, sends the line to the server, and then displays whatever the server sends back.
    Like all the applications described in this chapter, the echo application operates across a network. That is, the client and server programs can run on separate comput To reduce the size and make the code easier to read, the programs in this chapter use command-line arguments without checking their validity.
    ed to the Internet.

    Figure 3. 5 Illustration of the echo application, which can be used on any two computers connected to the Internet. The client program runs on one computer and the server program runs on another.

    To invoke the server, a user must choose an application number between and 32767 that is not being used by any other applications, and specify the number as a
    command line argument. For example, suppose someone using computer lancelot. cs. purdue. Edu chooses 20000 as the application number. The server is invoked
    by the command echoserver 20000 If some other application is using number 20000, the server emits an appropriate error message and exits ; the user must choose another number.

    Once the server has been invoked, the client is invoked by specifying the name of the computer on which the server is running and the application number the server is using. For example, to contact the server described above, a user on an arbitrary computer in the Internet an enter the command echoclient lancelot. cs. purdue. Edu 20000

    3. 8. 1 Example Echo Server Code
    File eclioserver. c contains code for the echo server.
    / * echoserver. c */


    #define BUFFSIZE 256

    wait for a connection from an echoclient and echo data

    echoserver O)

    (void) send (conn, buf£, len, O)
    senLeof (conn)
    return o

    As we have seen, the server takes a single command-line argument that specifies the application number to use. In C, command-line arguments are passed to the program as an array of strings (argv), along with an integer count of arguments (arge). The code extracts the command-line argument from argvtl], and calls the standard C function atoi to convert the value from an ASCH string to binary. It then passes the result as an argument to await_contact. Once the call to await_contact returns, the server repeatedly calls recv to receive data from the client and send to transmit the same◆

    * program-
    * purpose-
    * usage

    data back. The iteration terminates when reGv finds an emloFfile aril Teturns zero. At that time, the server sends an end-oFfile · and exits.

    3. 8. 2 Example Echo Client Code

    File echoclient. c contains code for an echo client application.
    / * echoclient. c */


    #de£ine BUFFSIZE 256

    #define INPUT_PROMPT”Input >”
    #define RECEDW_PROMPT”Received>”

    int readln (char *, int)

    * Program echoclient
    -Purpose contact echoserver, send user input and print server response
    * Usage echoclient [appnum]
    * Note Appnum is optional. If not specified the standard echo appnum
    (7 ) is used.


    maintint arge, char *argvt] )

    corrg) uter conp
    appnum w
    connection conn

    char buff [BUFFSIZE]

    int e»fpect, received, len

    if (arge < 2 I arge > 3 )
    (void) fprintf(stderr, usage %s «conDname> [appnum] \nll
    argv[O] )

    Sec. 3. 8 Code For An Echo Application

    / * c onver t the argument s to binary f ornnat c oltp and appnum */

    colt¥»-cname_to_coltg» (argv[1] )
    i£ (cottp ==-1)

    exit (1)

    if (arge == 3 )

    app-(appnum) atoi (argv[2] )


    if ( (app-appname_to_appnum (“echo” ) ) ==-1)
    exit (1)

    / * foLm a connection with the echoserver * /
    conn-make_contact (corg ), app)
    i£ (conn < O ) exit (1) (void) printf (INPUT_PRCMI?T) (void) fflush (stdout) / * iterate read input from the user, send to the server,"/ / * receive reply from the server, and display for user * / Whi le((1en-readln(bu£f, BUFFSIZE) ) > O)
    / * send the input to the echoserver */
    (void) send (conn, buff, len, O)
    (void) printf (RECEIVED_PRCM1?T)
    (void) fflush (stdout)

    / * read and print same no. of bytes £rom echo server */

    e]qect = len

    for (received = 0 received < e)qpect) len-recv (conn, buff, (expect-received) < BUE'FSIZE ? (expect-received) BTWSIZE, O) i£ (len < O) senLeo£ (conn) return (void) write (STDOUTJILENO, bu££, len) received += len (void) printf ( · \ n") (void) printf (INPUT-PROMPT) (void) fflush (Stdout) / * iteration ends when EoF found on stdin * / (VOd ) senLeof (conn) (void) printf ("\ n") return o The client progfam takes either one or two arguments. The fst argument specifies the name of a computer on which the server is running. If pfesent, the second argument specifies the application number the server is using. If the second argument is missing, the client calls appname_to-appnum with argument echo. After converting the arguments to binary form, the client passes them to make_contact, which contacts the server. Once contact has been established, the client issues a prompt to the user and enters a loop that reads a line of input, sends the line to the server, reads the reply from the server, and prints the reply for the user followed by a new prompt. When the client reaches the end of input (I. e., readln returns a zero value), the client calls send_eof to inform the server, and exits. Several details complicate the code. First, the client calls a. Function, readln, to read one line of input. Second, the client tests the return value from each function call, and exits when the value indicates an error occurTed. Third, the client calls Jluslz to ensure that output is displayed immediately rather than being accumulated in a buffer. Fourth, and most significant, the client does not merely issue one call to recv each time it receives data from the server. Instead, the client enters a loop that repeatedly calls recv until it has received as many bytes as it sent. The use of multiple calls to recv brings up a key point about our ApI A receiver cannot assume thlat data will arrive in the same site pieces as it was sertt ; a call to reen may return ess data than was sent in a colt to sen«L Later chapters explain why recv behaves as it does : networks divide data into small "packets". Thus, an application may receive the data from one packet at a time. Surprisingly, the opposite is also true : even if a sender calls send r e peatedly, the network software may receive data from many packets before the application calls recv. In