This assignment serves as an introduction to client-server programming using the TCP/IP protocols. In this assignment, you will write both a client and a server. Your client and server will communicate using TCP, and your server may implement any service you choose. The ground rules are simple: the server reads and writes data to and from the TCP connection. The server may prompt the client for input, or simply print a random message and exit. The exact details are your choice. Once you have a working server, you should leave it running in background (even after you log out) waiting for service requests from a client.
To access a service, the client opens a TCP connection to the server, sends and receives data, and then closes the connection. Conceptually, the client runs as a (very!) simplified version of telnet between your terminal and the server, copying data sent by the server to standard output and sending data read from standard input to the server. The client terminates the connection with the server when it receives an end-of-file from either the server or standard input.
The above scenario omits a key aspect of client-server programming: How does the client find out where the server is? That is, what transport-level address (e.g., Internet address and port number) should the client connect to?
One solution is to use a name server that dynamically maps service names into their transport-level addresses. We will supply you with a such a server, called oracle, allowing you to register the service you provide and advertise it to other students in the class. Conceptually, the oracle is like the white pages in your phone book. A server registers the name and transport address of its service in the phone book, and clients use the phone book to map service names to transport addresses.
When your server starts, the operating system will assign it an unused transport address (e.g., XXX) on which it can wait for incoming connection requests. The server then advertises the availability of its service by sending a short message to the oracle containing the name of the service (e.g., ``fortune'') together with the transport address (e.g., XXX). The oracle server records the name-to-address mapping in its local database.
When a client wishes to connect to a server, it first sends a message containing the desired service name (e.g., ``fortune'') to the oracle, and the oracle returns a message with the appropriate transport address (e.g., XXX). The client then opens a TCP connection to that service. Exact details for communicating with the oracle are described below.
Your client should accept the following set of commands from standard input:
When the client wishes to connect to a server, it performs the following steps:
Your server should perform the following steps when making a service available:
Oracle resides on machine spike.cs.duke.edu at well-known
UDP port oracle. All communication with oracle is via
UDP messages containing a structure called an om (for ``oracle
message''), whose definition can be found in the file oracle.h
in
narten/lib/cps214/p4. The file is reproduced
below:
#define luid 8
#define cchMaxServ 10
#define cchMaxDesc 40
#define verCur 'D'
#define cmdErr 1 /* An error occurred. See sbDesc for details */
#define cmdGet 2 /* Get the address of a service */
#define cmdAckGet 3 /* ACK for cmdGet message */
#define cmdEnd 4 /* Last response to a cmdGet message */
#define cmdPut 5 /* Register a new service */
#define cmdAckPut 6 /* ACK for cmdPut message */
#define cmdClr 7 /* Unregister a service */
#define cmdAckClr 8 /* ACk for cmdClr message */
struct om { /* oracle message */
unsigned long ti; /* time of registration */
unsigned char cmd; /* command/reply code */
unsigned char ver; /* version number of this structure */
unsigned short int port; /* TCP port number of service */
unsigned long int addr; /* Internet Address of server */
char uid[luid]; /* login of provider id (e.g., 'narten') */
char sbDesc[cchMaxDesc]; /* description of service (or error reply) */
char sbServ[cchMaxServ]; /* name of service requested/provided */
};
#define lom (sizeof (struct om))
To find a service, your client program fills in the fields of the om structure as follows:
In response to a cmdGet message, oracle returns two or more messages. Response messages have a cmd type of cmdAckGet, and the end of cmdAck responses is signaled by a cmdEnd message. CmdEnd messages do not contain the name of a service; they simply signal the end the last response. If only one service matches the client's request, the server will return two messages: a cmdAckGet, followed by a cmdEnd. Each cmdAck message contains the following fields:
When the server wishes to register a service with the oracle, it sends an om message with the following fields:
In response to a cmdPut message, oracle returns a message of type cmdAckPut if the registration succeeds. In the case of errors, the oracle returns a message of type cmdErr, and then sets the field sbDesc to contain a short explanation of the error.
The following steps may be helpful in getting started:
As an aid to debugging, as well as to trace use of your server, you should print trace output when connections are made. For example, you might print the host name and port number of every user that connects to your server, along with the time of the connection.
Requests to the oracle are actually a bit more general than we've described so far. Regular expressions can be used as service names or users ids to effect a kind of ``wild-card search'' for services. For example, specifying a service name of ``.*'' matches all services. Specifying a user name of ``...'' matches all services provided by users with three-character ids. The format of regular expressions is the same as that of ed(1).
Use your imagination and creativity in designing server programs. You may earn extra credit by implementing a ``neat'' server.
The following library procedures and system calls will be helpful: clearerr(3), gethostname(2), listen(2), close(2), getpwent(3), getuid(2), send(2), recv(2), getservbyname(3), gethostbyname(3), gethostbyaddr(3), getsockname(2), bind(2), socket(2), connect(2), accept(2), and listen(2). The routine select(2) can be used to read from both standard input and a TCP socket at the same time.
For more information about the Unix networking-related library routines and system calls, consult the chapter on sockets in Comer's book.
This document was generated using the LaTeX2HTML translator Version 96.1 (Feb 5, 1996) Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -split 0 p3.
The translation was initiated by Thomas Narten on Sun Dec 1 10:26:14 EST 1996