CSC2035 Assignment 2 Coursework Specification
A Reliable File Transfer protocol that works on top of UDP
Maximum mark: 100. This assignment is worth 17% of the overall module mark.
Due date: 15:00 Friday 16 December 2022 in NESS
Important
The specification of the coursework is this document plus the material on Canvas for Assignment 2,
including:
• The code base for the assignment
• The presentation introducing the assignment
• The preliminary UDP exercise for the assignment
You are expected to read all the information provided with the assignment.
Contents
1
Aim
................................................................................................................................................
1
2 Coursework Description
................................................................................................................
2
2.1 Introduction
.........................................................................................................................
2
2.2 Description of RFT protocol in the absence of errors (send_file_normal). .......................... 3
2.3
Server code
...........................................................................................................................
4
2.4 Client main function
.............................................................................................................
4
2.5 Non-documentation of functions provided for you ............................................................. 5
3
What you must do
........................................................................................................................
5
3.1 Functions to implement
.......................................................................................................
5
3.2 Logging client progress and error exit
..................................................................................
7
3.3 Summary of general hints
....................................................................................................
8
4 Building, running and testing your solution
..................................................................................
8
5 Marks
............................................................................................................................................
8
6 Assignment Submission
................................................................................................................
8
1 Aim
This coursework gives you the opportunity to develop your skills in code comprehension and in
systems and network socket programming using the C programming language.
The aim is to build a Reliable File Transfer (RFT) protocol that works on top of UDP to provide reliable
delivery of a file between a client and a server. This will give you some insight into the implementation
of reliable communications protocols. The library you develop will also show how a framework for
communications can be parameterised by different protocol implementations.
2
2 Coursework Description
2.1 Introduction
This coursework involves the completion of two file transfer protocols:
• Reliable file transfer (RFT) with positive acknowledgement in the absence of errors
(implemented in the function send_file_normal)
• Reliable file transfer (RFT) with positive acknowledgement and retransmission – an example
of a PAR protocol (implemented in the function send_file_with_timeout)
You can use the C programming environment you used for assignment 1 to complete this assignment.
To make sure you understand how to run client/server socket applications, you must do the UDP
preliminary exercise in Canvas and, ideally, you should attempt the extension to that exercise.
The code base for the assignment is in the zip archive csc2035-assignment2.zip with the following
files:
• Makefile – the project make/build file
• README.txt – the main README file with instructions for running and testing your solution
• README-client-*.txt – example log and error information output for the client
• README-server-*.txt – example log information output for the server
• in*.txt – text input files of various sizes that you can use to test your solution. Your solution
should work correctly for all the input files. The sizes of the different files have been chosen
deliberately to test various conditions. The size of a file is the number following “in” in the file
name.
• kill-rft-apps.sh – a shell script to kill rft_server and rft_client if “ghost” processes are left
running
• munit.c, munit.h – the test framework from https://nemequ.github.io/munit/
• rft_client.c – The client application code
• rft_client_logging.h, rft_client_logging.c – The client logging library for information output
and client exit on error.
• rft_client_util.h, rft_client_util.c – The client utility library with functions required for protocol
execution. rft_client_util.c contains the functions you must implement (this is the file you
edit).
• rft_server.c – The server application code
• rft_util.h, rft_util.c – Library of data structures and the checksum function shared by server
and client
• run-server.sh – A shell script to build the RFT client and server application and run the server
• run-client.sh – A shell script to run the client with convenient default options
• test_rftcu*.c – unit tests of rft_util.c functions
The above files are the starting point for your solution. To complete the project, you implement the
following 8 functions in rft_client_util.c:
• init_segment
• read_data
• send_data
• send_file_with_timeout
• send_metadata
• set_socket_timeout
• set_udp_socket
• close_protocol
3
You must implement the functions as specified in this document and according to their specifications
in rft_client_util.h. Do not change any of the other files provided or any other parts of rft_client_util.c.
You must not change function signatures. You will be marked at 0 if you do.
2.2 Description of RFT protocol in the absence of errors (send_file_normal).
This protocol uses UDP to send a file from a client to a server. The transfer starts by the client sending
the file metadata (size and output name) to the server. The client then sends the file in one or more
fixed sized messages that have a fixed sized payload buffer (some of which may be zero bytes). If the
file size is less than the size of the payload buffer, one file transfer message is sent. If the file size is
greater than or equal to the payload buffer size, multiple file transfer messages are sent. If the file size
is zero, the protocol terminates after sending the file metadata.
UDP is an unreliable connectionless transport layer protocol that has no means of guaranteeing
reliable delivery of a file. Networks can lose, corrupt or delay segments. To mitigate this to some
extent, the send_file_normal RFT protocol includes a positive acknowledgement from the server for
each file data message received. This means that:
• Data segments (of type DATA_SEG) are sent from client to server and ACK segments (of type
ACK_SEG) are sent from server to client. The segment structure and the types DATA_SEG and
ACK_SEG are defined in rft_util.h.
• The client does not send the next data segment unless it receives an ACK segment for the
previous data segment (the sequence number of the ACK segment should be the same as that
of the data segment previously sent).
• The data segment payload is the data that is read by the client from the input file. A segment
file_data field gives the amount of the payload buffer that is used for file data. One byte of
the payload buffer must always be reserved for the string nul terminator (0). Therefore, the
file_data value is always less than PAYLOAD_SIZE. After successful transfer of a file, the server-
side output file will be an exact, byte for byte copy of the client-side input file (including
whitespace etc.)
• For implementation of the protocol, the following additional information is sent as part of a
data segment: a sequence number, which should increase by 1 for each successful send of a
segment, and a checksum to detect any corruption in the data.
• The client must ensure that the payload of a segment is filled with 0s after any valid bytes
(that is the characters after the first file data bytes of a segment’s payload buffer). This allows
payload to be treated as a string by the server.
o Hint: the simplest way to achieve this is for the read_data function, that reads from
the file, to initialise the payload buffer to zeroes before each read from the input file
and to read less than the full buffer from the file on each read. The file should be
treated as a stream of bytes with no special significance given to a new line character
(for example).
• The send_file_normal RFT protocol assumes an absence of errors. This means that the
protocol does not handle errors during transmission of data across the network (e.g. no delay,
no loss of data segments, no loss of ACK segments and no corrupted segments).
You are given the implementation of send_file_normal. Do NOT change the send_file_normal
function. You do have to implement the functions that send_file_normal relies on:
• init_segment
• read_data
• send_data
• send_metadata
• set_udp_socket
• close_protocol
4
send_file_normal will not behave correctly until the listed functions are implemented.
2.3 Server code
You are given the code for the server side of the protocol (in rft_server.c). Do not change the server
code. Briefly, the server does the following:
• In main: checks that the number of input arguments is correct (i.e. the port number), then it
opens a UDP socket and binds to it because the client needs a fixed port number to send the
file to. The server then receives the metadata sent by the client (the output file name and the
total file size). The metadata struct defined in rft_util.h encapsulates the metadata. Once this
is received, the server is ready to receive the actual file.
• In receive_file: the server receives the file in segments, each of which has a payload of up to
PAYLOAD_SIZE – 1 file data characters plus at least one terminating 0 (see the segment struct
in rft_util.h). For each received segment, the server validates the segment as follows:
o checks the file data size matches the length of the significant characters of payload
received and that the data is nul terminated – the server exits with an error if these
checks fail,
o checks that the sequence number of the segment is the expected sequence number
(starting from 0 for the first segment) – the server exists with an error if this check
fails, and
o calculates and validates the checksum.
If the checksum for the data segment is valid, the server will write the significant bytes of
payload to the output file and it will send an ACK segment to the client with the same
sequence number as the one in the received data segment. This is to inform the client that
the data segment has been received correctly. If the checksum for the data segment is invalid,
the server will not write anything to the output file and will not send an ACK but will continue
to wait to receive file data. The server terminates on errors or once it has received all the file
data it expects (e.g. the number of bytes communicated in the metadata).
The behaviour of the server is the same whether the client is executing the protocol without timeout
(send_file_normal) or with timeout (send_file_with_timeout).
2.4 Client main function
You have also been given client application code in rft_client.c. The main function of rft_client.c will
first check that the user enters the correct number of arguments (inputfile, outputfile, etc. – see the
comments in rft_client.c). It then opens the input file for reading and then uses the following functions
from rft_client_util.c to do the file transfer:
• set_udp_socket
• send_metadata
• send_file_normal or send_file_with_timeout depending on transfer mode (called as the
send_file_op field of a protocol_t struct)
• close_protocol (called as the close field of a protocol_t struct)
You implement the functions in bold (along with 4 other supporting functions).
All information required for client side execution of a protocol is in a protocol_t struct that is defined
in rft_client_util.h. This includes a pointer to the function to use to execute the protocol (the
send_file_op field of protocol_t). See the header file for an explanation of the fields of the struct, and
rft_client.c, rft_client_util.c and rft_client_logging.c to understand the usage of protocol_t.
5
2.5 Non-documentation of functions provided for you
The following functions are provided for you:
• in rft_client_util.c: is_corrupted, verify_server, init_protocol, and send_file_normal
• in rft_client_logging.c: exit_err, log_protocol and log_separator (plus additional logging
functions that are private to rft_client_logging.c).
The functions have little or no documentation. This is deliberate. Part of the purpose of this
assignment is to test your code comprehension skills. Therefore, you will have to work out:
• whether to use these functions,
• where to use them,
• what they do from the examples of their usage and their implementations, and
• how to use them.
3 What you must do
3.1 Functions to implement
In file rft_client_util.c, you implement the functions described below and documented in
rft_client_util.h.
Important:
1. A pointer to a protocol_t struct (proto) provides the in and out parameters required for the
following functions. That is, proto fields provide the parameters to function execution. There
is only exception to this: init_segment requires two additional parameters.
2. You are expected to take the time to understand the protocol_t struct and its use and the
related proto_state enum and the meaning of the different states the enum values represent.
3. To complete the implementation of the following functions, there are cases where you can
use other functions that are provided in the code base (in addition to system library functions).
Writing your own code where you could use a function that is provided in the rft_util library
or in the rft_client_util library or in the rft_client_logging library will result in loss of marks.
When function documentation states that a function does something, this can mean that the
function calls another function to do something. It is part of the assignment for you to work
out which other functions to use and where to use them.
void init_segment(protocol_t* proto, seg_type type, bool payload_only)
• Initialise the file transfer segment of the given proto structure specified by the given segment
type (either SEG_DATA or SEG_ACK).
• This means zeroing the segment and setting its type to the given type. Or, if payload_only is
true, just zeroing the segment’s payload buffer
6
proto_state read_data(protocol_t* proto)
• Read the next fixed sized chunk of data from the given proto’s open input file stream into the
payload buffer of the proto’s data segment.
• The function returns a protocol state specified in the function documentation.
• Hint: You cannot use fgets because it stops reading on a line feed. There is a different library
function that reads n items of a specified size from a file stream. The size of each item of the
n items you need to read is the size of a char.
proto_state send_data(protocol_t* proto)
• Send the given proto’s data segment to the proto’s server.
• This function returns an appropriate protocol state if the current number of consecutive
retries/retransmissions exceeds the maximum retries allowed. The current
retry/retransmission count is updated by the send_file_with_timeout function.
• The function calculates the checksum of the data segment’s payload and sets the checksum
field of the segment. If the proto’s tfr_mode is not NORMAL_TFR_MODE then the function
will corrupt the checksum according to a loss probability. This corruption of the checksum is
used to simulate message loss and/or network errors for the send_file_with_timeout
protocol.
• The function returns a protocol state specified in the function documentation.
proto_state send_metadata(protocol_t* proto)
• This function sends the file meta data to the server (i.e. file size and output file name).
• A metadata struct defined in rft_client_util.h encapsulates the information to send to the
server.
• The function returns PS_META_SENT if sending metadata succeeds (the bytes sent is equal to
the size of the metadata struct) and PS_BAD_META otherwise.
• Hint: See how send_metadata is used in the rft_client.c and the fields of proto that are set
before its use.
proto_state set_socket_timeout(protocol_t* proto)
• This function sets a timeout for receiving a message on the proto’s socket. The duration of the
timeout is specified by the proto structure. The timeout means that a receive call using the
socket will return either when a message has been received on the socket or when the timeout
has expired, whichever is the sooner. A timeout is a property of a socket and only needs to be
set once.
• The function returns a protocol state specified in the function documentation.
• See documentation of set_socket_timeout and the setsockopt system call
proto_state set_udp_socket(protocol_t* proto)
• set_udp_socket opens a socket for the client to communicate with the server and fills in the
server address structure of the given proto struct.
• The function returns a protocol state specified in the function documentation.
void close_protocol(protocol_t* proto)
• Close open protocol resources.
7
proto_state send_file_with_timeout(protocol_t* proto)
• This executes the client side participation in the RFT protocol with positive acknowledgement
and retransmission.
• The function has very similar behaviour to the send_file_normal function with the following
differences:
• Simulation of segment corruption: Sending a file between a client and server on the
same machine is reliable. To be able to simulate the errors that may happen in
networks of different machines, this function artificially corrupts segments – the
proto’s tfr_mode causes the send_data function to randomly corrupt data segment
checksums.
• Timeout and retransmission: The client waits for a specific period of time for an ACK.
When the period times out (i.e. an ACK has not been received), the client resends the
data segment with a recomputed checksum.
• Exit on reaching the consecutive retry limit. The proto struct specifies a limit to the
number of consecutive retries/retransmissions that should be allowed. Another field
of the struct maintains a count of the current number of consecutive retries. The
number of retries exceeding the retry limit is one of the reasons that the send_data
function will return with a state other than PS_DATA_SENT. You need to the update
and reset the current count of retries appropriately in send_file_with_timeout and
react appropriately in send_file_with_timeout if send_data fails.
• Hints:
o For the protocol to work correctly, you will have to update fields of the proto
struct (including its state) during execution of the send_file_with_timeout
function. The proto fields also control information that is output by logging
and exit functions (see Section 3.2). You also update fields that summarise
information about protocol execution (see send_file_normal).
o send_data handles checksum corruption
o set_socket_timeout sets the timeout property of a socket
o You must work out how the result of a receive for an ACK indicates that a
timeout has occurred, and, therefore, that no ACK has been received.
Important:
• There is no need to compute the number of data segments/messages that are required to
complete a file transfer. Do not attempt to do this. You just need to know the total number
of file data bytes (the size of the file) that must be transferred and how many file data bytes
are transferred in each segment.
• Except when the file size is less than the data segment payload size, you must not read the
whole file at once. The transfer is tested with small files. However, it should work exactly the
same for large files (e.g. megabytes or terrabytes of data). It would not be possible to read
such files into memory all at once. Also, the protocol deliberately reads and sends a small
number of bytes at a time to be able to test send and ACK for small files. There are no special
cases here. Do not read the whole file at once. Read up to an appropriate data size for the
payload buffer before sending each segment.
3.2 Logging client progress and error exit
During execution, both client and server log information to stdout, by default, and errors to stderr
(usually causing process termination). In the client, this is achieved using the log_protocol and err_exit
functions that are declared in rft_client_logging.h. The README-client-*.txt files provide examples of
this log and error output. As with send_file_normal, logging information output and exit with error
should be part of the implementation of the send_file_with_timeout function.
8
As you can see from code provided, functions in the rft_client_logging library require the name of the
source code file that triggered logging or error output and the line in the file. There are examples of
how to specify the source code file name and line in the code that is provided.
Documentation of a function can specify that errors cause the client to exit with an appropriate error
state. This means that you use an appropriate function of the logging library to cause the client to exit
with an appropriate error message. The error message is typically determined by the protocol state.
It is your task to determine which state is appropriate for a given error. Possible states are values of
the proto_state enum.
To decide where to use logging and exit functions:
• Read the documentation of the functions you are implementing
• Look at and copy usage in the send_file_normal function (as appropriate)
• Try to replicate the output given in sample output provided with the initial code base (see the
code base README files).
3.3 General hints
• To understand what you need to do and how to do it look at the code that has been provided
for you.
• The code in the preliminary exercise gives a basic idea of socket-based communication
between client and server. It does not include error checking.
• The code in rft_server.c shows you how the server is set up and what it is expecting from the
client communication.
• The send_file_normal function is a very good starting point for send_file_with_timeout.
4 Building, running and testing your solution
See the README.txt file provided with the code base for information about running tests, running the
file transfer applications, and expected output.
5 Marks
The indicative contribution of each function to marks is:
• init_segment – 10 marks
• read_data – 10 marks
• send_data – 15 marks
• send_file_with_timeout – 30 marks
• send_metadata – 10 marks
• set_socket_timeout – 10 marks
• set_udp_socket – 10 marks
• close_protocol – 5 marks
An element of marks will be for good programming practice, including but not limited to: safe use and
choice of C library functions, not over-complicating solutions, reusing code and functions that are
provided for you (as appropriate).
6 Assignment Submission
Submit your rft_client_util.c file to NESS by 15:00 on Friday 16 December 2022.
Do not submit any other files. Anything other than the file rft_client_util.c will be ignored.