The material developed for this lab was developed by Prof. Felipe Perrone (Bucknell University). Some of this lab is based on material from Unix Network Programming Volume 1, by W. Richard Stevens, Bill Fenner, and Andrew M. Rudoff (Addison Wesley, 2004).
Permission to reuse this material in parts or in its entirety is granted provided that the credits note is not removed. Additional students files associated with this lab, as well as any existing solutions can be provided upon request by e-mail to perrone[at]bucknell[dot]edu.
TCP and UDP are two major transport protocols that are in use. We have worked with TCP measurement in our last lab. So far we have only used UDP once, in a small application. It's time to develop a better understanding of UDP and of how it compares to TCP. This lab will have you use UDP to rebuild the tools you developed earlier for TCP for performance analysis. This experience will help you put in perspective the differences between these two transport protocols, in terms of use at the API level and in terms of the performance characteristics of each one.
The second component of the lab is to experiment with the nature and the mechanism needed to send binary files across the internet. In the past labs and exercises, we have learned how to send character strings across different computers. Sending binary data in principle is no difference than sending strings of characters. However some detailed technical issues have to be resolved. For example, if one wants to send a linked list of nodes to a different computer. How do we do that? What does an address on one computer mean to another computer, sometimes in completely different architecture? If the length of the linked lists or the size of the nodes in the linked list is unknown ahead of time, how do we deal with that? These are some of the questions we would like to explore in this lab.
Create a directory in your local git repository called lab04, as in
~/csci363-s13/labs/lab04
Copy all files from ~cs363/Spring13/student/labs/lab04/
to your own lab directory. You should see two files, list.h
and list.c
that implements a basic linked list. You don't have to use these files if you have your own linked list implementation.
In this problem, you will create two programs called pingpong.c and reflectord.c, which communicate with each other using UDP datagrams. The general idea is that pingpong will create a message of a given length, transmit it to reflectord, which sends the same message right back to pingpong.
A pair of simple UDP programs (client/server) is available at the code example segment of the course website, which you might find useful.
(A) You will need to instrument pingpong to send a number of messages of a given size to reflectord and wait for it to come back.
UDP is a connectionless protocol, so you won't have to measure the overhead in establishing or tearing down connections (which you did for TCP).
For each message:
These time measurements can be obtained from placing calls to gettimeofday(3) in strategic places in your program. If you take these samples for each message sent from pingpong to reflectord, at the end of pingpong's execution you will report following value:
The command line for pingpong should be as follows:
pingpong [num_msgs] [msg_length] [host] [port]
where:
reflectord is running, and
Running from a shell should look like:
$ pingpong 50 10000 machine.eg.bucknell.edu
One or more parameters missing.
Usage: pingpong [num_msgs] [msg_length] [host] [port]
$ pingpong 50 10000 machine.eg.bucknell.edu 9010
0.034
In this example, 0.034 represents average the time to send and get back a message of 10,000 bytes.
(B) Your second program, reflectord, will be a daemon, that is, a server listening on a well known port and running in an infinite loop. This program should be very simple: wait for a connection request, establish the connection, listen for an incoming message, return the identical message to the sender, and go back to waiting for the next message.
The command line for reflectord should be as follows:
reflectord port
where:
port is the port number where reflectord is listening.
Create a Makefile for compiling your two programs. Also, make sure to use your evolving files wrappers.h and wrappers.c, and to augment them if you use any new system or library calls that you could wrap.
Run the client and server program with different message size (e.g., 100, 1000, 10000, ...)
Create a text file called answers.txt. In this file:
(A) Is there a limit to the size of the UDP datagram that you can send from pingpong? Use your best sources to investigate this question, experiment with your programs to verify whether your findings in the literature are accurate, and document in the answer to this question what you have learned/observed.
(B) Describe the data that your modified pingpong.c produces. You should be thinking of how to explain to someone who will never read your source code the interpretation of the numbers that the program produces.
(C) How are the time measurements you see here compared to what you had in the last lab with the similar program implemented in TCP?
When you're done with this problem, you need to do:
which should add a list of files to the git repository, including wrappers.h, wrappers.c, Makefile, pingpong.c, reflectord.c,
and answers.txt
.
In the past exercises, we have learned how to send character strings among network applications. A typical application scenario in which a sender sends a string of characters pointed by the variable buf
to a receiver looks as follows.
On the sender side:
char * buf = "hello world!"
size_sent = write(s, buf, strlen(buf));
On the receiver side:
char * buf = (char*)malloc(MAXSIZE+1); // don't know the size ahead of time
size_read = read(s, buf, MAXSIZE);
buf[size_read] = 0; // terminate the string
Now consider how we might send structured data that are not simply string of characters! For example, if a structure which can be used in a linked list is defined as follows.
If your program has a collection of data of the struct node_t
type, how do you send such a collection of data from a sender to a receiver? In this part of the lab, we will exercise on this topic.
In your answers.txt write the appropriate answers to questions below (not all need a text answer, as some will be programming.) You are asked to accomplish the following tasks, one at a time.
sizeof
operator to find out how many bytes of memory a structure defined above occupies. Explain why the structure takes this many bytes.First consider how to send one such piece of structure to a remote computer. Assume we are using TCP (UDP programming would be very similar), and you have established a connection between the sender and receiver properly. The general idea is that the sender will send a collection of memory bytes representing the data to the receiver. This collection of memory bytes starts with the memory location where the variable resides and the length of this collection is the size of the structure.
The key to solve this problem is to access the correct memory location where the data is stored. There are two possible ways to access the address of a variable. One is to use the address of operator ("&") to get the address of a variable; the second is to directly use the address of a variable assigned when using malloc()
or calloc()
calls. The following code segment illustrates the concept.
The receiver side can simply read the data into a properly defined variable of the same type.
How do we send a linked list of such nodes? The approach is to have the client traverse through the linked list from beginning to end, sending one node at a time to the server. Since we don't know the number of nodes in a linked list ahead of time, the server (receiver) side would have to loop on the condition that if more data is coming, the loop continues.
An alternative way to send or receive structured data is to store the received data in a generic buffer. When all is received, the receiver can recover the structured the data from the generic buffer. Try the following. On the client side, send one structured node as it was in the previous step. On the server side, instead of reading the data into the structured variable, define a generic buffer char * buf;
, allocate a maximum amount of memory to it, e.g., define and use a constant BUFFSIZE
of 4096 bytes. Once the data is read into the generic buffer, it can be casted as a piece of structured data as follows.
In your answers.txt explain how the last C statement works, in particular, what the phrase &(buf[total_count])
mean.
Now that you have a basic understanding how to pass structured data across the network, complete the lab exercises so that the client sends a linked list of structured data to the server. The server stores the received data in a generic buffer first, then recovers the linked list and prints out the contents.
Just for fun, you may consider sending the following Confucious quotes from one computer to another using the structure defined earlier in this lab. The variable len
can be changed to count
as an index, the variable rate
can be used to indicate a ranking. You don't have to use these quotes. Feel free to use any other you see fit. The exact meanings of these variables are not critical. The key is to pass a linked list of nodes, where each node contains a string (a quote), an integer, and a double.
Note that although the following example asks you send five nodes only, your program should be able to handle arbitrary number of nodes.
Submit list-sender.c, list-receiver.c, and updated answers.txt to your gitlab.
Extra credit: If you have time left, consider revising your program so that the program can handle strings of variable length. Note that the current program uses a structure that contains a fixed length (256) string. What do you need to do if the string length is not fixed? Try to have the receiver still use a generic buffer. Submit the finished programs using different pair of names list-sender-var.c and list-receiver-var.c.
Congratulations, you've finished this lab!