Lab UDP

Interprocess communication: UDP sockets

Goals

  • Learn to work with UDP sockets: You have worked with pipes and TCP sockets for interprocess communication, both of which use a byte-stream model. In this lab, you will work with UDP, a model based on “datagrams,” that is, units of packaged data. You will see that while, in some sense, UDP sockets are easier to set up, they don’t offer the same guarantees of in-order and reliable delivery, upon which you have been relying.
  • Practice with the concept of mutual exclusion: One of the advantages to using threads is that all your threads can share a common set of state variables (in this case, your server statistics). One difficulty arises, however, since mutual exclusion is needed in the access to shared variables by the concurrent threads of execution. If mutual exclusion is not used, your program is subject to race conditions and may not execute correctly.
  • Learn that not all protocols are ideal for all applications: You will implement a file transfer application two ways, using TCP and using UDP. From the perspective of programming, one is easier to complete than the other. From the perspective of performance, one may be better than the other. When you design a networked application in the future, you will need to consider your priorities before you select which transport protocol your implementation should use.

Credits

The material developed for this lab was developed by Prof. L. Felipe Perrone. Permission to reuse this material in parts or in its entirety is granted provided that this “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


Problem 1

When you design a client/server networked application, it is important to have responsiveness in the system. It is a fact that the server handles requests that are issued from clients. It is not a great idea, however, to have the server work directly on each request. The server can be more responsive if it can exploit concurrency and delegate each request to a new process or thread.

In this lab, we will develop a client/server file transfer application. When the client wants to make a request to the server, it will build a special message that contains one of two kinds of commands:

  • get file, which requests the transfer of a file from the server to the client host, or
  • get stats, which requests from the server utilization statistics.

Upon receiving a request, the server will create a new thread to carry out the appropriate actions.

The code given to you in files getfile.cfileserver.c, and getstats.c outlines an application for file transfer using TCP. Compile these programs and experiment with them: run a server using your self-chosen port number on a remote machine and connect to it with the client requesting the transfer of any text file.

In this problem, you will modify the programs given to you as specified below:

  1. Modify getfile.cfileserver.c, and getstats.c  to use the wrapper functions with error checking that you have been building up over the semester.
  2. Create a Makefile to build the executables corresponding to the three C files given to you.
  3. Modify fileserver.c to start up a new concurrent thread for every request it receives. Note that you need to limit the maximum number of concurrent threads your spawn so that your server is not easy prey to denial of service attacks. (Choose a reasonable limit for your number of server threads.)
  4. Augment the server in fileserver.c to respond to get status requests with the following information:
    • number of file requests served
    • number of status requests served
    • number of file not found errors
    • average length of files requested

Your work on (4) above depends on having the statistics kept in one or more data structures that are shared among the threads. Make sure to do the right thing. Nuff said?

One important issue to consider in this problem: How can you convince yourself (and the graders) that your program can serve multiple requests at the same time? You may think it through and give good reasons, or you may experiment with your program to demonstrate your conclusions.

When you are confident that you have debugged your code, create a text file called output1.txt with the record of a few sample runs of getfile and getstats for your threaded application. What you record in this file must be enough to demonstrate that your programs work correctly.

When you are done, you need to:

  • cd ~/csci315/Labs/Lab6
  • git add Makefile
  • git add getfile.c
  • git add fileserver.c
  • git add getstatus.c
  • git add readwrite.h
  • git add readwrite.c
  • git add output1.txt
  • git commit -m “Lab 6.1 completed”
  • git push

 

Problem 2

Create copies of the program files in your solution to Problem 1 with the following names:

  • getfile.c -> ugetfile.c
  • fileserver.c -> ufileserver.c
  • getstats.c -> ugetstats.c

Next:

  1. Adapt your Makefile so that it builds the executables that correspond to your new C files.
  2. Modify your multithreaded client/server to create a new implementation that uses UDP sockets instead of TCP sockets.
  3. When you are confident that you have debugged your code, create a text file called output2.txt with the record of a few sample runs of ugetfile and ugetstats for your threaded application. What you record in this file must be enough to demonstrate that your programs work correctly.

Here is the gist of what you need to keep in mind for this new version:

  • UDP works with datagrams, not a byte-stream. This means that you need to encapsulate the requests that go to the server into a structured message that contains a field with the type of the request. If the request is get file, the datagram needs to contain also the name of the file (you can assume that the file names are absolute paths and that it’s maximum length is 256 characters plus the null terminator for a C string. If the request is get stats, the datagram doesn’t need anything more than the request type.
  • When the server sends a file back to the client: it will come in discrete “chunks.” The client must reassemble the file from these discrete pieces. Keep in mind that UDP datagrams can only carry a certain maximum amount of data. If the file you are transferring is larger than what a single datagram can carry, it will be sent to the client as a sequence of multiple datagrams: all except for the last one will be full.
  • UDP makes no promises of reliable or in-order delivery, so your server datagrams to the client should carry a monotonically increasing sequence number. When the client receives those datagrams, it can look at the sequence numbers to see if they are arriving in order. If the client detects a break in the sequence or if the datagrams are out of order, the client can simply write to stdout that an error has happened and terminate.
  • What you put in each datagram can be structured into two parts. The first, at the start of the datagram, will be a small collection of control information (including at least a sequence number). The second will be just the data you are transferring.
  • A robust implementation of this application would not quit when there’s a break in the datagram sequence numbers. The client would request the server to retransmit lost datagrams and also buffer datagrams out of sequence so that they are written to the file in the right order. This level of sophistication is not required in the assignment, but you are encouraged to work it out.

When you are done you need to:

  • cd ~/csci315/Labs/Lab6
  • git add Makefile
  • git add ugetfile.c
  • git add ufileserver.c
  • git add ugetstatus.c
  • git add output2.txt
  • git commit -m “Lab 6.2 completed”
  • git push

 


Print Friendly