1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
| In this exercise you will use the basics of the Socket Interface to implement a simple File
Transfer Service (client and server side) over UDP transport protocol.
The Overview
In general, a file transfer service provides a user with the ability to move files between a
local and a remote hosts. The well-known examples are FTP and HTTP based file
transfer services.
As a rule, a typical file transfer service uses the reliable transport (i.e. TCP) to take
advantage of its reliability, performance and fair network sharing. A file transfer service
usually has a quite complex interface, enabling a user to read the contents of a remote
directory, to move up and down a remote file tree, to get files from the remote side as
well as to copy local files to the remote host.
In our first exercise we shall implement a very simple variant of a file transfer service
based on UDP. The File Transfer client requests from the server side either to send or to
obtain a specific file. In response, the File Transfer server either transmits or receives the
specified file to/from the client.
Notice that both the client side and the server side will be implemented in the same
source file. The same single program ft can be run either as a client or as a server. The
program will figure out its part (either client or server) at run time, from the command
line arguments.
The File Transfer Client
The File Transfer client uses the UDP transport service to communicate with the server
side. The client can perform 2 operations: either GET (to receive a file from the server) or
PUT (to send a file to the server).
For the GET operation, the client tells to the server the name of a file to be
moved. Then, the client receives the file from the server as the sequence of UDP
messages and saves it into a local file.
For the PUT operation, the client tells to the server the name of a file to be
moved. Then, the client opens a local file and sends its content to the server as
the sequence of UDP messages.
The synopsis of the program is:
ft <serv host> <serv port> <fname> [-o <save as>] [-m put] [-s < msg_size>] [-i < interval>]
where
<serv host> is either a server‟s domain name (such as [url]www.cnn.com[/url]) or its IP
address in the dotted decimal notation (e.g. 87.68.254.133)
<serv port> is a well-known server‟s UDP port to communicate with
<fname> is the name of a file to be moved (this is either a remote file on the server
side, or a local file on the client side)
<save as> (optional) is the name under which the file will be saved . If the
parameter is absent, the file will be saved under the specified fname name.
-put if this flag is present in the command line, the client will send the specified
file to the server (the PUT operation). Otherwise, the client will receive the
specified file from the server (the GET operation).
-s <msg_size> (optional) specifies the amount of file‟s data in each message
sent, in bytes (a positive integer number). If the argument is missing, the sender
should use the default message size of 1400 bytes.
-i <interval> (optional) specifies the amount of time in seconds to elapse
between sending 2 consecutive messages (a floating point number). If the
argument is missing, the sender should use the default interval of 0.001 sec (1
millisecond). Notice that zero value for the interval is valid and means that the
server sends its messages without any delay.
The File Transfer Server
The File Transfer server waits in an infinite loop for clients‟ requests for file transfers.
When a request from a client arrives, the server first looks at the requested operation
(either PUT or GET).
For the GET operation (the client wants to receive a file from the server), the
server opens the requested file, reads it and sends the contents as a sequence of
UDP messages to the client.
For the PUT operation (the client wants to send a file to the server), the server
opens a file with specified name and fills it with the data sent by the client.
The synopsis of the program is
ft <local port> [-s < msg_size>] [-i < interval>]
where
<local port> is a UDP local port to which the server will bind its listening
socket (an integer number)
-s <msg_size> is an optional argument specifying the amount of file‟s data
in each outgoing message, in bytes (an integer number). If the argument is
missing, the server should use the default message size of 1400 bytes.
-i <interval> is an optional argument specifying the amount of time in
seconds to elapse between sending 2 consecutive messages (a floating point
number). If the argument is missing, the server should use the default interval of
0.001 sec (1 millisecond). Notice that zero value for the interval is valid and
means that the server sends its messages without any delay.
The Protocol: GET
To initiate a file transfer, the client sends to the server a GET request. The GET
request message contains:
the code of a desired operation (GET). The code is a single byte with the
value 0x0.
the name of a desired file as a sequence of ASCII characters (NOT
including the trailing „\0‟).
Upon the receipt of a GET request from the client, the server tries to open the
target file for reading. If the operation succeeds, the server sends to the client a
confirmation message, containing the 4-byte number of bytes in the target file (in
the network byte order). If the server fails to open the target file, it sends back the
reject message containing a single byte 0xff.
Having sent a GET request message, the client waits for the server response. If the
response contains the error indication (0xff), the client issues an informative
message to stderr and exits with status 1. Otherwise, the client gets prepared to
receive the specified amount of bytes from the server. If no response arrives
during 10 seconds after issuing the request, the client times out, issues
appropriate error message and exits with status 1.
After sending a positive response for the GET request, the server in a loop reads the
target file sequentially from the beginning to the end and sends its contents to the client
as a sequence of data messages. Each message must be of a size specified in the
command line. Similarly, the time interval between any two consecutive messages must
be as specified in the corresponding command line parameter. The data message format
is as follows:
1 byte of status. Value 0x0 stands for „OK‟, whereas value 0xff stands for
„FAILURE‟.
If the status is FAILURE, the transmission has failed. The client should issue an
informative error message and exit with status 1.
If the status is OK
The 4 further bytes specify the location of the data in the file (as an
absolute offset within a file).
the rest of the message is the corresponding part of the target file
contents. It should be saved by the client in a local file at the specified
offset.
Having received a positive confirmation for its GET request, the client knows the target
file size. Then, in a loop, it receives a sequence of data messages from the server until
specified number of bytes are accepted and written to a proper place in a local file. If
during the process a client does not hear from the server for 10 seconds since last
received message, the client gives up, issues corresponding error message and exits with
status 1.
After getting through with a current client, the server returns to wait for other requests.
Having successfully received the requested file, the client issues to stdout the following
concluding message:
printf ("Received file '%s': %ld bytes, %lu pkts in %.3f sec (%.2f
KBytes/sec, %lu pkts/sec)\n",
specifying the target file name, its size in bytes, the total number of packets received
from the server, the total transfer time (from sending the request until the last byte is
written into the file), and file transfer rate in Kbytes/sec and packets/sec.
The Protocol: PUT
To initiate a file transfer, the client sends to the server a PUT request. The PUT
request message contains:
o the code of a desired operation (PUT). The code is a single byte with the value
0x1.
o 4 bytes of the target file size (in the network byte order)
o the name of a desired file as a sequence of ASCII characters (NOT including
the trailing „\0‟).
Upon the receipt of a PUT request from the client, the server tries to open the target
file for writing. If the operation succeeds, the server sends to the client the 1-byte OK
message (0x0). If the server fails to open the target file, it sends back the reject
message containing a single byte 0xff.
Having sent a PUT request message, the client waits for the server response. If the
response contains the error indication (0xff), the client issues an informative message
to stderr and exits with status 1. Otherwise, the client in a loop reads the target file
sequentially from the beginning to the end, and sends its contents to the server as a
sequence of data messages. Each message must be of a size specified in the command
line. Similarly, the time interval between any two consecutive messages must be as
specified in the corresponding command line parameter. The data message format is
the same as described earlier:
1 byte of status. Value 0x0 stands for „OK‟, whereas value 0xff stands
for „FAILURE‟.
If the status is FAILURE, the transmission has failed. The server
should issue an informative error message and exit with status 1.
If the status is OK
the 4 further bytes specify the location of the data in the file (as
an absolute offset within a file, in the network byte order).
the rest of the message is the corresponding part of the target
file contents. It should be saved by the server in a local file at
the specified offset.
After sending a positive response to the PUT request, the server in a loop reads the
target file sequentially from the beginning to the end and sends its contents to the
client as a sequence of data messages. Each message must be of a size specified in the
command line. Similarly, the time interval between any two consecutive messages
must be as specified in the corresponding command line parameter. The data message
format is as follows:
o 1 byte of status. Value 0x0 stands for „OK‟, whereas value 0xff stands for
„FAILURE‟.
o If the status is FAILURE, the transmission has failed. The client should issue
an informative error message and exit with status 1.
o If the status is OK
the 4 further bytes specify the location of the data in the file (as an
absolute offset within a file).
the rest of the message is the corresponding part of the target file
contents. It should be saved by the client in a local file at the specified
offset.
Having received the PUT request, the server knows the target file size. Then, in a
loop, it receives a sequence of data messages from the client until specified number of
bytes are accepted and written to a proper place in a local file. If during the process a
server does not hear from the server for 10 seconds since last received message, the
server gives up, issues a corresponding error message and exits with status 1.
After getting through with a current client, the server returns to wait for other
requests.
Having successfully transmitted a target file, the client issues to stdout the following
concluding message:
printf ("Sent file '%s': %ld bytes, %lu pkts in %.3f sec (%.2f
KBytes/sec, %lu pkts/sec)\n",
specifying the target file name, its size in bytes, the total number of packets received
from the server, the total transfer time (from sending the request until the last byte is
written into the file), and file transfer rate in Kbytes/sec and packets/sec.
The Gory Details
General
o UDP is a non-reliable protocol, which does not try recovering dropped or
damaged packets, reordering packets arrived in a wrong order and
throttling down transmission rate when receiver side does not keep up
with the sender. Hence, on the client side we must be prepared to handle
all these error conditions.
Using explicit byte sequence numbers in the data messages
provides protection against packet reordering
Using the timeout mechanism provides the simplest protection
against dropped packets.
o Check the command line arguments (both their number and the validity of
each one). In a case of any error, issue the “usage” message and exit with
status 1.
o Always check the return value of every standard function/system call. In
o
o
o
o
o
o
o
o
o
o
the case of an error, issue an informative message to stderr. If the error is
fatal, exit with status 1. Otherwise, keep on operating.
In the case of an error, use perror () when applicable to issue appropriate
error message. In other cases use frprintf (stderr, “...”).
Do not use “magic numbers” in the code. Instead, use #define and const
declarations.
When writing/reading to/from a file on disk, it is very important to
minimize the number of actual disk accesses to improve performance. Use
buffered I/O streams from <stdio.h> (fopen/fread/fwrite) rather than raw
disk I/O (open/read/write), since the performance gain of doing so is
significant.
As we have learned, sending 16 or 32 bit integer values through a network
requires conversion to the network byte order (the Big Endian). Use
htonl() in the server before sending the byte sequence number. Use
ntohl() in the client upon receiving the byte sequence number.
To implement the timeout mechanism, use select() function
To implement the delay between two consecutive messages, use function
usleep().
If a local file with specified name already exists, the receiver should NOT
overwrite it. In this case it will issue an error message and exit with status
1. See man page for function open() to find out how to open a file only if
it does not exist, with a single system call.
If an error indication is received from the sending side during the
transmission process, the newly created local file must be deleted. Use
unlink() function.
To set a file write pointer (in order to write to specific offset in a file), use
function lseek().
To obtain the size of a file, use stat() function.
The Client Side
o Since the client talks to a single server, you should use connected UDP
socket on the client side.
o To measure the elapsed time, use gettimeofday() function.
The Server Side
o As we know, a UDP socket can accept messages from different clients. To
keep things simple, we allow the server to handle only one client at a
time. No messages from other clients will be accepted while the server is
talking with a currently served client.
To achieve this, upon getting a new request, connect the server‟s
UDP socket to the client‟s address. This will guarantee that no
other client is able to break in.
Having finished with a current client, the server must disconnect
its UDP socket in order to allow the arrival of requests from other
clients.
As we have learned in the class, to disconnect a connected
UDP socket we should call connect() it to an address with
the address family set to AF_UNSPEC.
Relevant man pages
The Socket Interface: socket, bind, connect, sendto, recvfrom, write, read,
gethostbyname
The file system: open, read, write, lseek, fopen, fread, fwrite, feof, ferror
Timing: gettimeofday, select, usleep
The Class Solution and Testing
The class solution for the exercise is published at the course home page. Use Linux
executables ft to test your own programs. Remember: your client must work against the
class solution server, and your server must work against the class client. Make sure files
are transferred correctly: a target file and its copy must be identical. Ensure the identity
with diff / cmp or compare their checksums using sum.
Getting Started
Please, start working as soon as possible. It will be very hard to complete the
exercise in less than a fortnight, and almost impossible in less than a week.
However, starting on the next day will allow you to get through without much
pressure.
Start from implementing the client side, testing it against the class server.
Start from the minimal functionality, making it work properly without lingering
on secondary details. Work out them later, when you are sure you client program
works against the class server. |