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
You have learned that the Unix pipe is a construct for interconnecting two processes that execute on the same machine. Unix pipes follow the byte stream service model, meaning that you work with them by pushing bytes in on the write end and pulling bytes out from the read end. Since access to pipes is provided via Unix file descriptors, the programmer can use the same “file” read and write system calls to operate on them.
The concept of a TCP socket is very similar to that of a pipe. The most fundamental difference is that TCP sockets serve to interconnect two processes that execute on arbitrary machines. Whether the two processes execute on the same host or on networked hosts across the world from each other, the set up and operations on the sockets are the same.
You should think of a socket as a communication endpoint. If a socket interconnects processes on arbitrary hosts on the Internet, the first thing that should occur to you is that sockets must be related to Internet addresses. When we say Internet address, it might occur to you that we’re somehow referring to IP addresses, which we use to pinpoint hosts on the Internet. An IP address, however, can only identify a host, not an application process within that particular host. If you need to pinpoint a specific application process within a host, you need to extend this concept of address to the pair <IP address, port number>, where port number serves to identify an application within a host.
This mapping of application to port number doesn’t happen by magic, of course. An application must bind to a port number within a given host and it must choose a port number that is not used by the system for a standard service. Take a look at /etc/services to find a large number of well-defined ports that are used by standard applications. The port numbers you use should never conflict with these. In fact, you should be using port numbers in “user space”; pick something at random in the range 8,000 to 10,000 to experiment with your programs.
In this lab you will work with a pair of programs which implement the client/server paradigm. The server (echod) is a type of program known as daemon, which performs the following tasks on an infinite loop: wait for a request to arrive, process the request, and send back a response. The client (echoreq) is a program that crafts a request, sends it to the server, receives and processes the response, and then terminates.
The basic design pattern for client/server applications based on TCP sockets is illustrated in the figure below.
The figure shows the sequence of calls to functions in the socket library that are appropriate for the client process and for the server process. TCP sockets implements a high level abstraction that gives the programmer a byte-stream communication channel across networked hosts that is reliable and order-preserving. After the connection set up, you have something that works identically to pipes.
Note: You will need to turn in a Makefile that generates all the objects and the executables for this lab (and pre-lab) assignment. This includes a rule for building wrappers.o. Failing to include this Makefile will lead to additional issues in grading your lab.
Go through the code you have written for previous labs and find all the wrapper functions you wrote to substitute for system and library calls. Create two files with all the wrappers you have written:
Your files should include wrappers for the following functions: fork(2), pipe(2), wait(2), waitpid(2), open(2), close(2), write(2), read(2), connect(2), bind(2), listen(2), accept(2), and any others you use in this lab which set the “errno” variable when encountering an error condition.
First of all, you should implement the communication between the client and the server, that is, you will augment the two files given to you so that echoreq sends a string to echod, which receives the string and sends it back to echoreq without any changes.
Once that functionality works, you will add new functionality to echod. It’s a very familiar problem and you can reuse the code for tokenizing a string and eliminating extraneous spaces, which you wrote for Lab 2.
When the server receives a string with an arbitrary number of spaces between words, it will “clean it up” before returning to echoreq. By “clean up” you should understand that the string returned will have exactly one space between any pair of consecutive words.
For example, if the server receives a string such as:
this is a test of the emergency broadcast system
It returns to the client the following string:
this is a test of the emergency broadcast system
It should go without saying that your code for the solution to this and the next two problems should not use system calls directly. Instead, make sure to use your the wrappers you defined. They will enable your code to be more readable and to handle errors.
For the sake of debugging your code, you can put both your client and your sever in the same host: your very workstation. However, to verify that everything is working to specifications, make sure to put your client and your server each on a different host.
Create a file called lab4.txt to write answers to the following questions. Please write as concisely and clearly as possible.
Copy the echoreq.c file to a new file called echoreq2.c. Once you have discovered a modern alternative to gethostbyname(3), modify echoreq2.c so that it uses this new version. Otherwise, the behavior echoreq2.c should be identical to what you see in your solution to Problem 3.
When you are done with everything, you need to:
Before turning in your work for grading, create a text file in your Lab 4 directory called submission.txt. In this file, provide a list to indicate to the grader, problem by problem, if you completed the problem and whether it works to specification. Wrap everything up by turning in this file:
Yes, we have been using git primarily as a means for you to place your work in a remote repository that the graders can access. HOWEVER, we can’t forget that the primary benefit of a source version control system is to help out the developer, that is: YOU!
Consider committing your code (even if only to your local repository) as soon as you have determined that you got something working. Heck, you can commit also partially debugged versions of your code, if you want to create a checkpoint. This can help you immensely if you turn out to screw something up, by accident, and want to recover your files from a previous checkpoint. To recover the previous state recoded in your repositories, the commands below are useful:
Note: [up to -10 points] An incorrect or incomplete Makefile to build all programs in the lab assignment.