cs42200:spring19:labs:lab04

Lab04 : UDP ping broadcast to monitor a set of servers

  • Using UDP to communicate between client and server
  • Write a simple UDP ping client/server

Feb 20th (02/20/2019) - 11:59pm

Introduction

UDP (User Datagram Protocol) is a communication protocol that send messages as datagrams.

UDP is a connectionless protocol. In other words, UDP doesn't require a handshaking process which means UDP does not require the socket connect(), listen(), or accept() that you used for TCP in Lab 03. In UDP, a sender can just directly send messages with sendto() without any prior connection. A receiver only needs bind() to listen on a specific local port, and recvfrom() to receieve incoming messages.

There is one more thing to note about the connectionless paradigm. In TCP, when a connection is closed, it will be immediately notify the other. For example, when a sending application closes a socket, the receiving application is notified because recv() returns 0. In UDP, however, there is no way for an application to detect that the other end has stopped sending, because there is no connection.

UDP is unreliable: there is no guarantee of message delivery. In other words, a message sent with sendto() may not be delivered to the recipient at all. In contrast, in TCP, as long as the connection is established, any messages sent with send() will be guaranteed to be delivered.

You may wondered why we need UDP. Does it provide any benefit over TCP?

There are couple of benefits. First, UDP is lightweight. Although you may not notice, TCP does many things under the hood to make it reliable. TCP sends acknowledgements, runs retransmission timers, manageds timeouts, and avoids congestion. UDP doesn't do those things, which means, UDP takes less CPU and generates less background traffic than TCP. So, UDP can have lower latency than TCP.

Second, UDP doesn't require a time-consuming handshaking process to start or stop a connection. So if you just want to send a message, without caring too much about whether the message is delivered, but just want to send it immediately, UDP would be a better choice.

In today's lab, you will be implementing a server and a client based on UDP.

  1. To connect to a machine in the Xinu lab: ssh [Put Purdue Account Name Here]@[Put Target Computer Here]
    1. For example:arastega@xinu1.cs.purdue.edu
    2. Note: Target computer should be one of the xinu machines (xinu01-xinu21)
  2. You will be prompted for your password
  3. Once the password has been verified, you will gain access to the remote system
  4. You should already have directory cs422 under your home directory (provided you completed lab1 correctly)
  5. Download lab04.tar.gz with wget under ~/cs422/. Note that option '-O' is the letter O and must be capitalized.
    cd ~/cs422/
    wget http://courses.cs.purdue.edu/_media/cs42200:spring19:lab04.tar.gz -O lab04.tar.gz
  6. Untar the downloaded file.
    tar xvf lab04.tar.gz
  7. Navigate to the apps directory under lab04.
     cd ~/cs422/lab04/apps 
  8. You will find two empty files under this directory: pingserver.c and pingclient.c . Your code for server and client will go in these files. Use echoserver.c and echoclient.c as hints on how to write the code for this lab.
  9. To compile the files:
    cd ~/cs422/lab04/compile_linux/
    make pingserver pingclient 
  10. If compiled without errors, this will generate two executables: pingserver, pingclient.

The ping program uses the ICMP protocol to determine whether a specified endpoint is alive. When a host or router receives a ping request, IP software will return a reply known as an echo. The arrival of a reply lets the client know that the remote machine is alive.

In this exercise, you will write both a client and a server that use UDP to implement the same basic idea. Instead of merely targeting one computer, however, your application will find out how many servers are alive at a given time. That is, you will generate and send a request to which all servers respond. Your client will collect multiple responses.

Let's take a look how your program should look.

The client will send a request message that contains a 32-bit unsigned integer with value 1. Transmission will take place over UDP. Note that the message (“1”) will contain exactly 4 bytes. Please don't include any other integer variable or newline (\n) character. Also, when sending the message 1, please send it as a a 32-bit unsigned integer (uint32_t).

The server will basically receive a request change 1 to 2, and send the message back to the client. However, a given server may go to sleep periodically. While it is sleeping, a server will not receive messages from the client, and will not send a reply back to the client. After sleeping, a server will remain awake for a given seconds. While it is awake, the server will be able to receive messages and send replies back to the client.

For example, if the server was able to receive a message during the awake from the client, then server will send back a message with value 2.

When the client receives a reply from a server, the client will compute the round-trip delay, and print the delay. For example, if the client receives 2 from 11.11.11.11, client will print out the latency from the server:

Reply from server 11.11.11.11, latency : 10000usec

If the message is from 22.22.22.22 and latency was 32000usec, then you should print out its IP as follows:

Reply from server 22.22.22.22, latency : 32000usec

The server takes three arguments as follows:

./pingserver  application_number  awake_period_in_seconds  sleep_period_in_seconds

For example, to start a server that listens on port XXXX and cycles remaining awake for 10 seconds and then sleeping for 5 seconds, the command is:

./pingserver XXXX 10 5

The server will cycle between being awake for 10 seconds and sleeping for 5 seconds. While it is awake, the server will receive messages and echo the messages back to the client. The server will never terminate until it is killed (e.g., with Ctrl-C).

The server does not take any input from stdin.

The client takes takes two or more arguments as follows:

./pingclient  application_number  hostname1_to_ping  hostname2_to_ping  hostname3_to_ping ...

For example, to use port XXXX and ping messages to sslabXX and mooreXX, the command would be:

./pingclient  XXXX  sslabXX  mooreXX

For this lab, the port number used by the client, XXXX, must exactly match the port number used by the server.

As consequence of using the same port number in both the client and server, it will not be possible run both a client and server on the same computer. That is, once one of them uses bind() to specify a port number, the other one will receive an error message when attempting to bind to the same port. Please use multiple lab machines in sslabXX, mooreXX, or xinuXX to run multiple servers and a client at the same time.

Your client should accommodate up to 12 server hostnames as arguments. Please use argc and argv to find all host names.

The server algorithm:

  • Create UDP socket and bind to the specified port.
  • Use recvfrom() to receive the next incoming message and sendto() to send a reply back to the client.
  • Repeat the above during the awake time, and then go sleep for the sleep time. Repeat the cycle forever.
  • Do not read from standard input.

One challenge when implementing a server is to make the server sleep for a specified period of time. To do so, the server can use the sleep() function.

One other problem arises because recvfrom(). recvfrom() is a blocking function. That is, the server will wait indefinitely until it receives a message from a client. Independent of whether messages continue to arrive, the server should remain awake for the specified period. How is such behavior possible?

One way involves the use of setsockopt() to set a receive time-out for the socket (argument SO_RCVTIMEO is used. If a time-out had been set for a socket, recvfrom() will return after the time-out, even if no message has been received.

The pseudo-code below may help you construct a server.

/* -  Create a socket and bind the server's address to it.
   -  Use the timeval struct to store the values of sleep and awake time periods.
   
*/

  while(1)
  {
  
    err = setsocketopt(sock, SOL_SOCKET, SO_RCVTIMEO, &awake_time_period, sizeof awake_time_period)
  
    /* - Get the current time (t1)
       - Start receiving packets using rcvfrom 
       - Check the return value of recvfrom and number of last error (errno), if it is EAGAIN or EWOULDBLOCK call sleep().  
       - Get the current time again (t2), update awake_time based on the 
         difference t2 -t1 (i.e. the awake_time_period = awake_time_period - (t2 - t1))
       - The server replies back to the client with an 32 bit unsigned integer number (value = 2)
    */
  
  }



The client will do:

  • Create a UDP socket to bind to get echo'ed messages.
  • Use a for() loop to send ping messages to the all hostnames given in arguments. Client must send all ping messages at once.
  • Use a for() loop to receive echo'ed messages from servers. Print out the responded server's IP address and delay.
  • Because recvfrom() is blocking call, a user may use Ctrl+C to terminate a client.
  • Client will terminate once all servers have responded
  • Do not read any input from standard input.

To turn in your work, use the turnin command to submit your entire directory.

cd ~/cs422
turnin -c cs422 -p lab04 lab04

You can check the results with turnin -v.

turnin -c cs422 -p lab04 -v
Grading Rubric (tentative) Points
TA Test cases +15
Overall organization of the program +2
Coding style, commenting, etc +2
Makefile, compile and run +1
  • cs42200/spring19/labs/lab04.txt
  • Last modified: 2019/02/12 09:35
  • by arastega