C代写:CS441 Reliably File Transfer

代写基于UDP的可靠网络传输程序,需要实现上层的可靠协议。

Description

In this program, you will be using the socket API to transfer a file reliably from one machine to another. You will write two programs, a file server and a file client. The client will request the file and the server will provide it. Your programs will use UDP sockets, which provide unreliable connection-less service to the application layer. You will essentially be building a reliable data link layer (Protocol 3) on top of the socket interface. Remember that Protocol 3 is stop and wait and that it corrects for lost frames by implementing a timeout at the sender which retransmits lost frames if no acknowledgement is received. Since our campus networks are actually quite reliable and seldom lose frames, you will emulate losses by discarding frames randomly at the client. Your programs will then recover from the lost frames.

I am providing you with two UDP programs, a sender and receiver. When run, the sender will send one packet to the receiver. You may use these as a starting point and modify them to provide the required service. Your programs must run on two separate machines (that’s the point of networking).

Client

  1. The client program must prompt the user for the name of the computer where the file server is located and the port number that the server is running on (for example, csweb01 and port 12000) or just read the values off the command line.

  2. The client will communicate with that machine using a UDP socket.

  3. The client will then need to create a request frame to send to the server. We’ll just assume that the server knows which file to send back (although you could build in the extra functionality to specify the file name if you like.) This request frame is sent unreliably. We’ll just assume that the client resends it if it does not hear back from the server. You do not need to implement the retransmission logic on the client.

  4. After the request has been received, the server will begin to send the file in stop and wait fashion in chunks of 50 bytes each. So the server will send frames of 51 bytes (a 1 byte header containing the frame sequence number and 50 bytes of data). Each time a frame is received, the client will randomly decide whether to discard it (to emulate a frame loss). You should implement a 20% chance of loss. If the received frame is chosen to be a loss, it is discarded and no feedback is given to the server.

  5. If the frame was not discarded, the client should inspect the sequence number to ensure that this is the frame the client is waiting for. If it is, the client should then send an acknowledgement that the frame was received successfully so that the server knows to send another frame. These ACKs are pure ACKs so they only contain the sequence number of the frame being acknowledged (1 byte). If the sequence number of the frame is incorrect, the client should just discard it without sending anything back to the server.

  6. If the frame was valid, the client should write the frame payload to the output file.

  7. When the last frame is received, the socket and the file should be closed.

Server

  1. The server program should be started first and sit in passive wait on its UDP socket until the client contacts it.

  2. After receiving a frame from the client, the server should open the file and read it into consecutive frames (51 bytes each consisting of a 1 byte header containing the sequence number and a 50 byte payload) to send back to the client. The server should wait 1 second between packet sends. It can do this by calling sleep(1) after each sendto() call.

  3. The server expects to receive an ACK for each frame received by the client. The server retransmission timer should be 2 seconds, so if an ACK is not received within 2 seconds, the frame must be resent by the server (using the same sequence number.) Since the server will sleep for 1 second after each send, it should then wait up to 1 second after it wakes up for the ACK. I have described below how you might implement this wait.

  4. After the file has been completely transmitted, the socket should be closed.

Output

Each program must print out an indication of events as they occur as shown below. Use exactly the format shown below.

The File client must print a line showing the sequence number of each frame received, the time received, and the first 5 bytes of the payload. Also, print a line each time an ACK is sent indicating the frame being acked and the time. Lastly, when the client decides to throw away a frame, print a line indicating the sequence number and time.
Example:

Client received frame: Sequence number = 1 Time = 15:22:34 Packet contents = AAAAA
Client sent ACK: Sequence number = 2 Time = 15:22:35
Client received frame: Sequence number = 2 Time = 15:22:36 Packet contents = BBBBB
Client discarding frame: Sequence number = 2 Time = 15:22:36

The File server must print a line showing the sequence number of each frame sent, the time sent, and the first 5 bytes of the payload. Also, print a line when a timeout occurs, and indicate the sequence number of the frame being retransmitted, and the time. Lastly, print a line each time an ACK is received indicating the frame being acked and the time.
Example:

Server sent frame: Sequence number = 1 Time = 15:22:34 Packet contents = AAAAA
Server received ACK: Sequence number = 1 Time = 15:22:34
Server sent frame: Sequence number = 2 Time = 15:22:35 Packet contents = BBBBB
Server timeout: Sequence number = 2 Time = 15:22:36
Server received ACK: Sequence number = 2 Time = 15:22:36

Notes

I have posted my lecture from Chapter 6 in which I discuss the socket interface. It would be useful to listen to it. You may also read sections 6.1.3, 6.1.4 (sockets) and 6.4.1 (UDP) in both the 4th and 5th edition of the textbook. They are very short. All socket calls are also described in the man (manual) pages available on the Linux machines and here: http://www.die.net/doc/linux/man/

Your programs must be run on the COS Linux machines. Of course, you can access these machines remotely using putty as you did in the Program #2.

Do your development incrementally. So:

  1. Get usend and urecv compiled and working. The headers of the files describe how they should be run. At that point you have 2 programs communicating - that’s a good start.

  2. Modify the programs so that they alternate sending and receiving (essentially passing frames back and forth.)

  3. Add the sequence numbers to the frame header and add logic to verify the correct order of frames and acknowledgements.

  4. Add in the retransmission timer and buffering.

  5. Add in random losses.

  6. Add in the file I/O (reads and writes to file). Do this last as it is the least important part.

As you add functionality, make sure you always keep a working version so that you can hand in something on the due date. Always hand in something because partial credit is much better than a 0.

If you are unfamiliar with C function calls, here is an excellent link to on-line man pages, specifically for Linux. http://www.die.net/doc/linux/man/

The various C and C++ compilers on the Linux machines are called gcc, cc, g++ (and possibly others.) I use gcc myself. See the man pages for details.
A makefile is always a good idea. Here is an example. Also see the man page on makefiles.

1
2
3
4
5
6
# CS 441 Project 2
all: usend urecv
usend: usend.c
gcc -Wall usend.c -o usend
urecv: urecv.c
gcc -Wall urecv.c -o urecv

An easy way to implement the 20% chance of loss is to call random() which returns a random number from 0 to 4 billion. You mod the result with 5, which gives you a number which is 0,1,2, 3, or 4, each with a 20% probability. You can say then, if the number is 0, then that frame is a loss. See man page for random().

1
2
rnum = random();
if ((rnum % 5) == 0) {frame should be discarded}

One issue on the server is: how do you wait on a timer and wait for the ack at the same time? An easy way is to use the select() function. (See man page on select(2).) It allows you to block on a file descriptor, which sockets count as, for a given number of seconds, in our case 1 second (after the 1 second sleep). If no data shows up (no ack arrives) by the timeout, it returns. You must set things up first using FD_ZERO and FD_SET before calling select(). Here’s a link to webpage which gives an example of using select(): http://support.sas.com/documentation/onlinedoc/sasc/doc750/html/lr2/select.htm

To print dates and times, see the man page for ctime(3).

Since you have 1 byte for your sequence numbers, number your frames from 0 to 255. You can assume that no file will be so big that the sequence numbers will roll over.

Do not hard-code the network addresses into the code. Read the port numbers and IP addresses/hostnames from the command line (as is already done in the example programs.)

Use the attached input file as the file to transfer. It is small (1600 characters) and only should take about 32 frames to transmit. With 20% loss, you should see about 6-7 frame losses.

You program must be written in C (or C++).

Running urecv and usend

First FTP urecv.c and usend.c to the Linux machines and compile them. Then set up a second putty session to a different Linux machine. You’ll run usend on one machine and urecv on the other. The instructions for running each program are given in their headers, but here is an example:

Urecv takes one command line argument, the port number it should try to bind to locally. This number must be between 1000 and 64000. So you could run urecv as “urecv 21000”.

Usend takes 2 command line arguments. Since it is sending to urecv, it needs to know urecv’s address, which is a host name and port number. If urecv was running on hopper (one of the Linux machines) and has bound to port 21000, then usend could be run as “usend hopper 21000”. Note that you must run urecv first and then usend.

Here are some example scripts showing their (minimal output) and assuming that we are using hopper and turing:

[kbrown@hopper F17]$ ls
urecv urecv.c usend usend.c
[kbrown@hopper F17]$ urecv 15000
Socket has port #15000
Received packet, time = 1384742302 9948, seqno = 1
Received packet, time = 1384742329 75023, seqno = 1 (Hit ctrl-C to kill process)
[kbrown@hopper F17]$ exit


[kbrown@turing F17]$ ls
urecv urecv.c usend usend.c
[kbrown@turing F17]$ usend hopper 15000
sent packet with seqno 1
[kbrown@turing F14]$ usend hopper 15000
sent packet with seqno 1
[kbrown@turing F14]$ exit

To submit: (All files should be text (.txt or .rtf), .pdf, or Word (.doc, .docx) format.)

  • The commented code for your server and client.
  • The output of your server and client including the commands you used to invoke them and diff results on your transmitted and received file.
    You can generate an output script by executing the “script” command before invoking your program. When your program is done, type “exit” to end the script. The file will be called “typescript”. You can also name the scriptfile by using one of the options to the command.
    So you might ssh to hopper (a Linux machine), start the script session, and run the server on this machine. You could then ssh to turing, start the script, and run the client.
    After your file is transferred, kill your processes (Ctrl-C).
    Diff the transmitted file and the received files to show that they are the same. The diff command checks 2 files to see if there are differences. For instance, “diff inputfile outputfile”. See man page for diff command.
    Exit from the scripts and you’re done!
  • A file containing an explanation of your results. On which Linux machines did you run your programs? (Again, you must run your programs on 2 different Linux machines. You may not run both on your laptop say.) How many frames were lost? Were any frames lost that your receiver did not intentionally throw away? Do you think you would enjoy implementing Protocol 6?