CS 325 Assignment #3
Broadcasting on the Faux RF Layer
Due Monday, October 9th by 11:59pm
Not accepted after October 12th
Introduction:
For this assignment you will create a multi-threaded application that both broadcasts data via the simulated RF layer, and simultaneously watches for broadcasts from other "hosts". While this assignment isn't officially part of the class project, it will give you experience with some techniques and code that will be required once you start on the 802.11 implementation. In particular:
- Compiling with and using the simulated RF layer
- Writing multi-threaded applications
- Converting integer data to arrays of bytes and vice versa
- Getting threads to sleep for fixed intervals
The application you write for this assignment will repeatedly broadcast a "packet" consisting of a simulated MAC address and an eight-byte value representing the current time, but will also watch for transmissions from other applications and display them as they arrive. The application should accept a specific MAC address from the command-line input or, if none is provided, will select a MAC address randomly. For full credit, your program should contain two threads: One that sends packets at random intervals, and a second thread that constantly watches for incoming transmissions and prints them as they arrive. The specifics are spelled out below, following the Resources section.
Resources:
- RF layer Documentation.
- A project archive that you can import into Eclipse, containing everything you need to get started. (See tips below for guidance on importing.)
- A BlueJ-flavored project for those of you using BlueJ.
- The project includes sample Java code showing the RF layer in use.
- Our sample threads code is all online for reference as well.
- A
jar
file containing my solution to this assignment. (Run it from the command line like this: java -jar time.jar
, otherwise you won't see its output.)
Specifics:
-
Your program must consist of at least three classes: A class called
TimeServer
containing the main method, that processes the command-line arguments, starts the threads, and waits for them to complete. A class containing methods for creating and broadcasting packets at random intervals, and class containing the code that watches for incoming transmissions and prints their contents. Those last two will need to implement the Runnable
interface so that they can be run concurrently.
-
Your application should allow a MAC address (an unsigned 16-bit integer) to be specified on the command line. If none is specified, you should select one randomly. This MAC address will be sent as part of all transmissions your program makes. (This isn't expected to be a real MAC address — we're just using the 16-bit value to uniquely identify each TimerServer host.)
-
All "hosts" must use the same packet layout: A two-byte integer (a
short
) representing the sender's MAC address, followed by an eight-byte integer (long
in Java) that represents the current time in milliseconds. (The clock()
method in the RF class provides eight-byte time values.) Be sure to use network byte order when constructing packets! For example, the high-order (most significant) byte of the MAC address should be the first byte in the packet, followed by the low-order byte. The eight-byte time value should be ordered in the same way.
-
The "sender" thread should create a 10-byte packet as described above, transmit it, then select a random wait time between 0 and 7 seconds and sleep until it's time to send again. It should stay in this infinite loop until the program is killed. For full credit, make sure your random delay is at a finer resolution than seconds. That is, a delay of 3.5 seconds should be possible, or 4.253 seconds, etc. You should also print a message that contains the integer values being transmitted so that other hosts can verify that they received the data correctly. (See my sample output below.) Mine also prints out the contents of the 10-byte packet as individual unsigned byte quantities, which you might find helpful during debugging.
-
The "listener" thread should make a call to
receive
and block until a transmission arrives. It should then extract the MAC address of the sender and the time value, and print both as integer values. As with the "sender" thread, the listener should be an infinite loop that runs until the program is killed. (Note: After 20 minutes, the RF layer will kill itself just in case you've left one running by accident. This will kill your program as well if you leave it around for more than 20 minutes.)
-
Your program should print error messages if an incoming packet has the wrong size, or if the call to
transmit
doesn't send the correct number of bytes.
Sample Output
Here's some sample output from a run of my program. I didn't specify a MAC address, so the program chose one randomly (319). The line about the RF layer being initialized is printed by the RF layer itself when the constructor runs. I printed messages from each of my threads so I knew that the "reader" and "writer" were both running. In this example, another host was running (MAC address 39296), so we see the output from my program as it receives packets from host 39296 every few seconds. The clock value is in milliseconds. It doesn't start at zero, but you can subtract one value from another to get an elapsed time. Different hosts might disagree slightly about the current time, but that's ok — we'll deal with that later in the project.
brichards@laptop[201] : java -jar time.jar
Using a random MAC address: 319
(That's 0x13f in hex)
RF layer (v2.2) initialized at Tue Sep 19 19:37:13 PDT 2023 (/192.168.1.62/-16577)
Writer is alive and well
Reader is alive and well
Sent packet: 319 1695177436163 [ 1 63 0 0 1 138 176 114 236 3 ]
Sent packet: 319 1695177437405 [ 1 63 0 0 1 138 176 114 240 221 ]
Received [ 153 128 0 0 1 138 176 115 4 219 ]
Host 39296 says time is 1695177442523
Sent packet: 319 1695177441558 [ 1 63 0 0 1 138 176 115 1 22 ]
Received [ 153 128 0 0 1 138 176 115 32 15 ]
Host 39296 says time is 1695177449487
Sent packet: 319 1695177448967 [ 1 63 0 0 1 138 176 115 30 7 ]
Sent packet: 319 1695177453246 [ 1 63 0 0 1 138 176 115 46 190 ]
Sent packet: 319 1695177456796 [ 1 63 0 0 1 138 176 115 60 156 ]
Received [ 153 128 0 0 1 138 176 115 82 241 ]
Host 39296 says time is 1695177462513
Sent packet: 319 1695177463563 [ 1 63 0 0 1 138 176 115 87 11 ]
Received [ 153 128 0 0 1 138 176 115 105 211 ]
Host 39296 says time is 1695177468371
Sent packet: 319 1695177468541 [ 1 63 0 0 1 138 176 115 106 125 ]
Received [ 153 128 0 0 1 138 176 115 126 94 ]
Host 39296 says time is 1695177473630
Sent packet: 319 1695177472645 [ 1 63 0 0 1 138 176 115 122 133 ]
Sent packet: 319 1695177474826 [ 1 63 0 0 1 138 176 115 131 10 ]
Sent packet: 319 1695177479534 [ 1 63 0 0 1 138 176 115 149 110 ]
Received [ 153 128 0 0 1 138 176 115 178 106 ]
Host 39296 says time is 1695177486954
Sent packet: 319 1695177486364 [ 1 63 0 0 1 138 176 115 176 28 ]
Sent packet: 319 1695177488848 [ 1 63 0 0 1 138 176 115 185 208 ]
Received [ 153 128 0 0 1 138 176 115 199 83 ]
Host 39296 says time is 1695177492307
Sent packet: 319 1695177494324 [ 1 63 0 0 1 138 176 115 207 52 ]
^C
Notes:
-
If you use Eclipse, you can import my starter code as an Eclipse project. Save TimeserverProject.zip to your computer without unzipping it. In Eclipse, use the File/Import... command and select "Existing Projects into Workspace" under the "General" tab. On the next dialog box, click "Select archive file" and use the Browse button to find the .zip file. If you use BlueJ, download this project as a starting point instead. On other IDEs, you should probably start with the BlueJ version of the project, but you'll have to add the rf.jar file to your classpath manually. (See me if you can't figure out how to do that.)
-
Your program should only create one RF instance! The sender thread and receiver thread should be using the same RF, which means you'll want to create it in your main program and pass it as a parameter to the constructors of your sender and receiver.
-
Your receiver won't "see" your sender thread's transmissions. That's by design. (An Ethernet implementation behaves the same way, for example.) You'll only receive timestamp packets from other timeserver programs running on the same machine, or on other machines within the same network.
-
There's no need to wait for the channel to become idle before sending. You'll have to pay attention to that in your actual 802.11 projects, but here you can just send at random intervals without checking the channel. If two timeservers' transmissions overlap both transmissions will be lost, but that's a feature, not a bug.
-
Converting integer values to sequences of bytes can be a little tricky, but you can use bitwise operators like
&
, |
, and >>
to extract the fields you need. (For example, consider what 7241 & 0xFF
gives you.) Java only supports signed types, which is going to cause us some grief when working with bytes and shorts. For example, an unsigned short should allow any value between 0 and 65535, but Java's signed short ranges from -32768 to 32767 instead. You might find it useful to use a "bigger" sized integer type to hold results before turning them into bytes, though you'll still need to enforce the proper limits on its size.
-
Make sure you test your code thoroughly: Run multiple copies of your application and verify that the broadcast mechanism is working as expected. It would be a good idea to verify that your application inter-operates with both my code and other student versions. Don't panic if some of your transmissions go "missing" — collisions between hosts will cause packet loss.
Submitting:
I want you to submit your code, as well as a file capturing the output of your program as it sends packets and receives them from other hosts. Zip up your entire project directory and submit that via canvas. Make sure you've submitted everything I'll need to compile and run your code!
Brad Richards, 2023