Telcom 2300
Homework #9 - Simple Socket Program
1. Goal:
To use network data communications and understand
basic client-server concepts.
2. Background:
There are many methods two programs can use to
communicate with each other.
One common method is to connect the two programs with a
TCP connection, which
allows a stream of data to be sent from each program to
the other.
The de facto standard interface between a user's
program and the operating
system's networking software is the Berkeley socket
interface.
There is a library which is available to us which
encapsulates the Berkeley
socket interface into a simpler (although more
limiting) interface. It is
called 'libutil'.
The functions in 'libutil' are described more fully
in 'Internetworking with
TCP/IP Volume III' by Comer & Stevens.
2.1 The functions:
These are the functions that 'libutil' provides that
we are interested in for
this homework:
int passiveTCP(char service[], int
qlen);
int connectTCP(char host[], char
service[]);
In addition, one of the functions from the Berkeley
socket interface will be
needed:
int accept(int msock, struct sockaddr *fsin, int alen);
And we will still need to use the Unix I/O functions 'read' and 'write':
int read(int desc, char buffer[], int
len);
int write(int desc, char buffer[], int
len);
2.2 The Functions Used By the Server side:
The 'passiveTCP' function creates a passive TCP
socket. The prototype for
this function is:
int passiveTCP(char service[], int qlen);
It returns a descriptor to the passive socket (also
called the master socket).
If there are problems, then the return value is
negative and the return value
is not a valid descriptor.
The first parameter describes the service or port
number that is associated
with the master socket. We will use an ASCII
character string holding the
port number assigned in the class meeting, for
example "5100". Note that
this is not an integer 5100.
The other parameter is the number of clients that
can be queued for connection
before operating system refuses any more
connections. We can use 5 for this
parameter.
The 'accept' function waits for a connection from a
client. When it is called,
the program blocks (waits indefinitely) for a
connection from a client. The
prototype for this function is:
int accept(int msock, struct sockaddr *fsin, int *alen);
When a client connects, 'accept' returns a
descriptor to an active socket
which is connected to the client. This descriptor
can be used in the 'read'
and 'write' calls just like a file descriptor.
This descriptor *cannot* be used
with the 'lseek' call.
The first parameter is the passive socket which was returned by 'passiveTCP'.
The second parameter is the address of a structure
which will be filled in with
the IP address of the client machine (we will not be
needing the information
returned in this structure).
The third parameter is related to the second
parameter. When called, the
'alen' variable should hold the size of a 'struct
sockaddr' structure. When
'accept' returns, it holds the number of bytes which
were filled into the
'fsin' parameter by 'accept'. As with the second
parameter, we will not be
needing the value returned by this parameter.
More information is available about 'accept' from
the usual sources: man
pages (man -s 3socket accept), the Answer Book and
others.
2.3 The Functions used by the Client side:
The 'connectTCP' function initiates a connection
from the client to the
server. The prototype for this function is:
int connectTCP(char host[], char service[]);
When 'connectTCP' is called, one of two things will occur:
If the server is up (it has called 'passiveTCP' and
is waiting in 'accept')
then the connection is made, and 'connectTCP' returns a
descriptor to an
active socket which is connected to the server.
Note that at the same time,
the server will return from 'accept' with a descriptor
which is connected to
the client side. At this point communication can
occur between the two
programs.
If the server is not up (it is not running or for
some other reason it has
not called 'passiveTCP'), then after some time
'connectTCP' will return a
negative value to indicate that it has not successfully
established a
connection with the server.
The first parameter is the name of the computer that
is running the server
program - for example "paradox.sis.pitt.edu" or
"rotor.sis.pitt.edu".
The second parameter describes the service or port
number that is associated
with the server. The client uses the same ASCII
character string as the server,
which represents the port number assigned in the class
meeting - for example
"5100". As above, note that this is not an
integer 5100.
2.4 Functions used by both the Server side and the Client side.
Once a connection is established between the client
and server programs, then
each of them can use the 'read' and 'write' function
calls to exchange data.
The descriptor of the active socket is used just like
the file descriptor is
used when we read and write from a file. Note
that we *cannot* use 'lseek'
which is specific to files.
After the client calls 'write', the data in the
buffer gets sent to the
server. The server can call 'read' to get the
data.
Similarly, after the server calls 'write', the data
in its buffer gets sent
to the client. The client can call 'read' to get
the data.
The read function is blocking. If one side
calls 'read' before the other side
calls 'write', then there is no data to be read. The 'read' call
will block (wait
indefinitely) until some data arrives after the other side calls
'write'.
After the client and server are finished
communicating, they each should call
'close' to properly terminate the connection between
them.
3. Requirements:
You are to write two programs which exchange
information from an RSVP list.
One of the programs will be the client side and the
other will be the server
side.
The server side program will wait indefinitely for a
client to connect to it.
When the client connects, it will read one name record
from the client.
Then it will open an RSVP list file and append the new record to
the end of the file. Finally, it will send the
entire contents of the RSVP
list to the client. It can then close the
active socket and return to waiting
for another client.
The client side program will read a name record from
a file. It will open
a connection to the server and then send that record to the server.
The client will then receive all of the records from
the server into a buffer.
The client side will print the records from the server
to the screen.
This illustrates how the programs will interact:
1. The server has no RSVP list entries on its first startup.
2. The client sends one entry to the
server. The server adds it to the empty
file and returns the entire file (one record) to the
client. The client
prints one record to the screen and exits.
3. The client sends another entry to the
server. The server appends it to
the file and returns the entire file (now two records)
to the client. The
client prints two records to the screen.
etc.
When the server exits, its RSVP list file should
hold all of the names sent to
it by the clients.
The server program should take one command line
argument - the port number.
It should use this parameter when calling 'passiveTCP'.
The client program should take three command line
arguments - the file name
which holds the name record, the name of the machine
where the server
program is running and the port number used by the
server.
Hints:
You can start with the skeleton program
(server-form.c) for the server side, which
was discussed in the class meeting. You would
need to implement the 'dataxfr'
function which is called after a successful call to
'accept'. This function does
all of the data transfer with the client, and handles
the file.
The server side does not need to parse the
records. It can just receive the
new record from the client, append the new record to a
file, and then send
everything in the file to the client.
The client side does not need to parse the record it
sends to the server. It
can just read it from the file and send it to the
server.
Pay close attention to the lengths when you use 'read' and 'write':
- Your client must tell 'write' exactly how many bytes to send to the server.
- When you are calling 'read', you don't know how
many bytes you will read.
You should always be ready to read more bytes than you
expect. As with
a file, 'read' will tell you exactly how many bytes it
has actually read.
You must decide if you want to use a NULL character
when you send a record
from the client to the server. If you do
this, you need to be careful how you handle
it when the server writes the record to the file.
If you are working in the lab and run your server
program on the machine that you
are logged in to, then the name of that machine is the name you should
give to your
client. In this case you should not have your client connect to
paradox. You can
get the name of the lab machines by looking at the label that is
attached to each
of them.
4. Deliverables:
Email your source code files, any header files and a
shell script which builds
the executable to the GSA.
Test Input Files
a.cdb
b.cdb
c.cdb
d.cdb
e.cdb
f.cdb
libutil library
Server Skelton