Changelog:
- 14 Feb 2023: refer more clearly to protocol section from exercise
part; note about submitting to the submission site and requirements for
that; adjust intro to refer to
mailbox
model. - 15 Feb 2023: add the parenthetical
(plus a checksum)
an additional tim in the protocol description - 15 Feb 2023: note explicitly that recvd is called when packets are received.
- 15 Feb 2023: add note about wrong len for
fwritein some versions of the template code.
In some versions of the lab template code before approx 4pm 15 Feb, the
fwriteusesleninstead oflen-3. If note corrected this may be the cause of some extra junk in the output on./netlab 2.
You’ve already worked with TCP sockets in CSO1. In this lab you’ll
learn how to add reliability on top of unreliable mailbox
type
model of a network.
You’ll have enough to do in this lab, we’ll not worry about doing it over an actual network. We’ve provided a simple network simulator for you.
Download netlab.tar on a linux system (e.g., with
wget https://www.cs.virginia.edu/~cr4bd/3130/S2023/files/netlab.tar).Extract it and enter the directory (e.g., with
tar xvf netlab.tar; cd netlab).Test it with
make ./netlab 0You should see a welcome message appear, ending with a
!.Edit
netlab.cso that you also see messages for./netlab 1,./netlab 2, etc. See below for a description of the messages the (simulated) server you are communicating with expects, including when you need to send acknowledgment or resend requests if you don’t receive a response.Show a TA your code or submit it to the submissoin site.
- We expect everyone to finish
./netlab 1and to make good progress on./netlab 2 ./netlab 3is quite a bit harder and is recommended if you have time- If you are submitting instead of checking off, we require
./netlab 1and./netlab 2to be complete
- We expect everyone to finish
1 Our Driver
We provide a network simulation driver program. It has the following pieces:
Function
void send(size_t len, void* data). You’ll invoke this to simulate sending unreliable packets over a network.Function skeleton
void recvd(size_t len, void* data). This function is called by our simulation code when packets are received. You’ll fill in details to describe what you would do on a packet receipt.Function
int setTimeout(void (*callback)(void*), unsigned long ms, void *arg). You may invoke this to ask to havecallbackinvoked aftermsmilliseconds (ms * 0.001seconds), passingargas the argument.On success, returns a positive identifier that may be used in
clearTimeout. On failure, returns a special error code:0ifcallbackisNULL-1ifmsis too far in the future (more than a minute)-2if you have too many pending callbacks scheduled (based on an implementation-defined limit guaranteed to be at least 16)
The callback is executed once per
setTimeoutcall, assuming it is not cleared withclearTimeoutfirst.Function
void clearTimeout(int timeoutID). IftimeoutIDwas returned bysetTimeoutand the callback has not yet been invoked, unschedules that callback. Otherwise, does nothing.Function
void waitForAllTimeouts(void). If there are no pending timeouts, returns immediately. Otherwise, blocks until there are not scheduled timeouts left.Note, if you do not call this function then your program will stop before it receives any messages.
2 Protocol
Every message must have its first byte be a checksum. We’ll use a very simple checksum for this lab: the xor of all other bytes.
To send the array of bytes [0, 1, 2, 5] you actually
send a five-byte message: [0^1^2^5, 0, 1, 2, 5].
You should send the server a 4-byte message (plus a checksum) to
initiate conversation. The first three bytes should be the ASCII
characters for GET; the fourth should be an ASCII digit
0 through 9. The server will then start
sending you messages in discrete packets.
The first three bytes of each packet the server sends will be a checksum, a (1-based) sequence number, and the total sequence count. Both sequence number and sequence count will be encoded directly as a byte, not using ASCII.
If the server plans to send two packets, one containing
[3, 1] and the other [4, 1, 5], they will
actually arrive as [1^2^3^1, 1, 2, 3, 1] and
[2^2^4^1^5, 2, 2, 4, 1, 5].
After receiving a message, you should reply with a four-byte message
(plus a checksum). The first three bytes should be the ASCII characters
for ACK; the fourth should be the sequence number you
received. If a message is not delivered, you should reply with the
ACK of the last message you got in order (or re-send the
GET if the very first message is not delivered). However,
messages may be delayed in transit and may arrive out of order. You
should wait at least a few seconds before deciding a message will not
arrive and re-sending its request.
Each GET will give a different message, and with a
different level of errors you need to handle.
- sends the full message without errors
- sends the message without errors, one a packet at a time, requiring
you to
ACKproperly - sends messages unreliably; some messages never arrive and need to be re-requested
- sends messages unreliably; some messages arrive in a corrupted state and need to be re-requested
You may assume that if a message has not arrived after a full second, it will not arrive.
3 Tips
Work with a partner.
Messages with bad checksums are ignored by the server.
You only have two kinds of messages you’ll ever send. It’s probably worth writing helper functions to send them (or just one helper to send both based on an argument).
ACK0 does not work; useGETinstead.Make a simple program that uses the
setTimeoutto print something or the like, just to make sure you understand how it works.The following prints 3, then 1, then 4, ending two seconds after it started.
void pnum(void *num) { printf("%d\n", (int)num); } int main() { int one = setTimeout(pnum, 1000, (void *)1uL); int two = setTimeout(pnum, 500, (void *)2uL); int three = setTimeout(pnum, 100, (void *)3uL); int four = setTimeout(pnum, 2000, (void *)4uL); clearTimeout(two); waitForAllTimeouts(); }The argument of your callbacks must be typed as a
void *, but (via casting) can be any type of 8 or fewer bytes. Feel free to useNULLif you don’t need an argument.My solution used a few global variables for the information I wanted in every callback. It was 58 lines of reasonably-formatted C code.