This lab is much shorter than the other labs to give you the opportunity to prepare for your second checkpoint during the lab this week. Keep in mind that this content will be very relevant for your second assignment, so remember to come back to it later.
Outline#
In this week’s lab, you will:
- Learn the system calls for network programming in Linux
- Learn to use a networking API for performing client/server transactions
- Hack an echo client-server application to gain practical experience with writing network applications
- Set up and run and modify a tiny web server
Introduction#
We will look at two network application today that are based on the canonical client-server model. Recall from the lectures that clients and servers use the sockets API for establishing a connection. Once a connection is established they perform transactions by sending and receiving messages using a file descriptor.
The client and server both use the socket() system call. The client further uses the connect() system call for connection with the client. The server uses the bind(), listen(), and accept() system calls for listening to and accepting connections from clients. Refresh your knowledge of a typical client and server workflow in socket-based communication.
In this tutorial, we provide you with existing code for two network applications. These applications make use of wrappers on top of the systems calls for socket-based communication. We first want you to be comfortable with the API the application use.
In the lectures you have seen similar examples of networking code utilising the
csapp library. In the lab8 directory we have provided you with a subset of
this code that is relevant for the exercises this week. If desired, you can
access the full csapp.c and
csapp.h files.
Open the lab directory and investigate the code for open_clientfd() and open_listenfd() in network_utils.c. Make sure you have a basic idea of what these functions are trying to accomplish and how to use them in a network application.
Exercise 1: Echo Client/Server Application#
An echo client-server is an application that allows a client and a server to connect so a client can send a message to the server and the server can receive the message and send, or echo, it back to the client.
You are given the code for the client side in the echoclient.c file. The
program receives a host name and port number as arguments, which it will use to
open a connection. If a connection is successful it will then read lines from
standard input using the getline function. Each line will be sent to the server
using rio_writen. The response it receives from the server will then be printed
to stdout.
If you’re not familiar with getline, remember to read the manual page
by running the command man getline in your terminal.
You are also given the unfinished code for the server side in echoserver.c. It
takes a port number as an argument, which it uses to open a listening socket.
The server waits for connections using the accept function. When a client
connects, it will first print out the hostname and port number the client has
connected to.
Your task is to complete the echo function in echoserver.c so that it will
read data sent by the client (using the connfd file descriptor) and send it
back to the client.
Complete the echo function in echoserver.c.
Testing the Echo Client/Server Application#
First, let’s make sure the echo client and server can communicate properly on your local machine.
- Start the
echoserverin a terminal using any port that works for you. Avoid popular ports already taken (e.g. 25 or 80). We recommend using a port number > 1023. - Start
echoclientin a separate terminal. Make sure you set thehostnameargument to “localhost” and use the same port number as you did to launchechoserver. - Make sure (by observing the messages printed on the screen) that the client-server connection is established.
- Type a message on the client side and make sure the message is echoed back by the server.
Push your changes to echoserver.c to GitLab. The CI will run a simple test by
launching the server as a background process
and attempt to initiate a connection to it using echoclient.
Exercise 2: Networking Questions#
Rather than more coding practice, this exercise will require you to think about questions regarding networking and the sockets API.
Is it possible to modify the echo server to send the clients IP address and port number to the client before echoing any messages? Identify the relevant code on the server side that will need to be changed.
Servers are designed to run for a long time without stopping. Therefore, they need to provide good service to all their clients regardless of client’s actions. Examine the echo client and server code and list anything you can think of that a client might do to cause the server to provide poor service to other clients. Suggest improvements to fix the problems you identify.
Once you have determined a way for a client to cause the server to provide poor service to other clients, test it on your implementation.
Why does the socket interface use listening and connected sockets to serve
client requests? What would be wrong with having a server create a socket, set
it up using bind() and listen(), wait for a connection, send and receive
through that socket, and then when it is finished, close it and repeat the
process?
Discuss your answers with a tutor or your fellow students.
Exercise 3: Echo Server With Metadata#
Your next exercise is to modify the echo server so that it sends the number of bytes it received from a client before echoing the response. For example, if a client sends the following:
./echoclient 127.0.0.1 1234
Hello, World!
The server should respond with the following:
Received 14 Bytes: Hello, World!
Copy across your code from echoserver.c to echoserver-metadata.c and modify
the implementation so that it will send the size of the message before echoing
the message.
Exercise 4: Tiny Web Server#
Web clients and servers interact using an application-level protocol called HTTP or hypertext transfer protocol. HTTP operates on top of the TCP/IP stack. HTTP works as follows. A web client (browser) opens an internet connection to a server and requests content. The web server responds with the requested content and then closes the connection. The browser displays the content on the screen. Web content is written in a language called hypertext markup language or HTML. Note the distinction between the protocol (used for communication and to request content and respond to content) and the language for displaying content.
The lab code repository includes a tiny web server. Open the file and inspect
the code. Make sure you have a full understanding of what the key functions in
the tiny web server are trying to accomplish. In particular, focus on the
handle_client, serve_static and serve_dynamic functions.
Let’s make sure the web server works properly on your local machine.
- Compile
tinyby running the commandmake tiny. - Start
tinyby running the executable and using a port number of your choice in a terminal, similar to the echo server. - From your web browser type
localhost:port#to point the browser (client) to the webpage tiny (server) is serving. This will display the contents of thehome.htmlfile present in your directory. - Inspect the output from tiny in the terminal and make sure a connection is established and a transaction took place.
Exercise 5: Dynamic Content#
The tiny web server is able to serve dynamically generated content as well as static content.
Compile the adder program by running make adder and launch the tiny web server
again. Observe what happens when you visit a url of the form
localhost:PORT_NUM/cgi-bin/adder?N&M (where PORT_NUM is the port number the
server is running on, and N/M are integers). Can you identify what the
server uses to determine a dynamic request is being made, and how it runs the
adder program to retrieve a the result?
You may find reviewing Lab 4 useful for understanding this code.
Using adder.c as a template, write another program sum_range.c that is
invoked by visiting the URI /cgi-bin/sum_range?N&M&S (where N, M and S
are integers). Given N and M, it should produce a HTML page that
presents the result of summing all integers in the range N to M inclusive,
with a step count of S.
You should be able to complete this task in such a way that requires no
modifications to be made to tiny.c.
You should also endeavour to make your program as robust to “bad input” as possible. For example, how will your code handle a request for a step size of 0?