[ This content is protected and may not be shared, uploaded, or distributed. ]
(Please also check out PA3 FAQ.)
Lab 6 has four parts:
Part A (lab6a) - introduction to multithreading (useful for PA3):
This part has no coding.
Please do the following:
Make sure you know why some of the programs crashed and know how to fix them and you can decide
how you would prefer to create threads and join threads in the next part of this lab and in the
remaining programming assignments.
Part B (lab6b) - simple multithreaded web server (useful for PA3):
In this part of the lab, you will start with your web server in Part D of Lab 4 and change it into a multithreaded web server.
We will use the same commandline syntax, i.e., "lab6b port_number". This lab is web server-only.
Please continue to use "lab4data" as the root directory of your server for the rest of this lab.
Your simple web server in Part D of Lab 4 can only handle one web client connection at a time.
When accept() returns a valid socket file descriptor, this socket is passed to the
talk_to_client() function to talk to the client. Your web server uses talk_to_client()
to read an HTTP request from the client and send an HTTP response back to the client. When you finished talking to the
client, you shutdown and close the socket and you return from talk_to_client(). Then your server
go back and call accept() again to talk to the next client.
In order to talk to multiple web clients in parallel, when accept() returned a valid
socket file descriptor, you must create one child thread to handle that connection and talk to
the client at the other end of the connection. Each child thread must use talk_to_client()
as its first procedure. This way, when your child thread is about to return from talk_to_client()
(i.e., is about to self-termiante), it would have shutdown and closed the socket.
By the way, in Part D of Lab 4, you shutdown() and close() the socket when talk_to_client() returns.
Now you are doing multithreading and you need to delete those two calls since you are not calling talk_to_client() directly.
(If you keep you calls to shutdown() and close() right after you have created a child thread to handle the connection,
the socket may become unusable even before the child thread starts running!)
So when should your server shutdown() and close() a connection? Well, never, for this lab! We will address this in Lab 7.
If you are getting crashes soon after you have added code to create threads, please take a look at
this PA3 FAQ item. Please understand
that for this lab, your web server should never self-terminate, and therefore, you won't get a chance
to join with the child threads you have created (and you don't need to).
As a result, if the destructor of a thread gets called,
your program will crash and die in it (i.e., die in "std::thread::~thread"). Therefore, you must
make sure that the destructor of a thread is NEVER called! For this lab, the basic solution is to add a newly
created child thread to a global list AND make sure that the destructor of that list is NEVER called!
To make sure that we can see parallel downloads, you must limit the data sending rate of your
server to be approximately 1,024 bytes (or 1KB) per second when you are sending a requested file to
the client. Therefore, after your server have sent an HTTP response header with a status of
200 OK, your server should stay in a loop and read 1,024 bytes
of data at a time, send it to the client, and sleep for one second (by calling usleep(1000000)).
When you create a connection-handling thread, you must assign it a unique connection number to identify this connection.
The first connection should be connection number 1. You can use a global variable called next_connection_number.
Every time you assign the value stored in this variable, you must increment it by 1.
Please print the following line to cout after you have obtained the URI from a request line in talk_to_client():
[#]\tClient connected from IP:PORT and requesting URI\n
where # is the connection number associated with this connection,
IP and PORT are obtained from get_ip_and_port_for_server() in my_socket.cpp,
and URI is from the request line in the HTTP request header.
Every time after you send 1KB of the response body to the client and before you call usleep(),
please print the following line to cout:
[#]\tSent ??? KB to IP:PORT\n
where # is the connection number and ??? is
the number of times you have called better_write() to send 1KB (or less for the last chuck of data) of response body to the corresponding client.
Please also print the following line to cout right before talk_to_client() returns
(i.e., right before your thread self-terminates):
[#]\tConnection closed with client at IP:PORT\n
where # is the connection number.
The executable of your web server for this lab exercise must be named lab6b.
Please also create a Makefile so that when the user types "make lab6b",
this executable will be generated.
When you are done with implementing lab6b, please do the following:
- Change directory into the "lab6" directory mentioned above.
- Copy your sourt code and Makefile into this directory.
- Since this lab is originally based on Part D of Lab 4, you will use the same test data.
Please download lab4data.tar.gz into that directory and type:
tar xvf lab4data.tar.gz
This should create a subdirectory called "lab4data" with a bunch of files in it.
- Type "script lab6b.script" to start a transcript. Then type:
uname -a
cat /etc/os-release
make clean
make lab6b
./lab6b 12345
- Run two more Terminal windows and cd into the same "lab6" directory.
- In the second Terminal window, type:
wget -O x http://localhost:12345/textbooks-3-small.jpg
- As quickly as you can, type the following in the third window:
wget -O y http://localhost:12345/textbooks-2-small.jpg
- Make sure wget is seeing 1KB/sec in both windows (you should try to get within 10% of 1KB/sec, at least in the middle of the downloads when
throughput has stablized).
Since "textbooks-3-small.jpg" is about 14 KB long, download it should take about 14 seconds.
If you don't start downloading "textbooks-2-small.jpg" soon after you started the first download,
there may not be too much parallelism. In that case, you should re-run the steps above.
- Wait for both downloads to complete then do:
diff x lab4data/textbooks-3-small.jpg
diff y lab4data/textbooks-2-small.jpg
The above command should produce no printout. If there is any printout, please fix your server code and try again.
- In the second Terminal window, type:
wget -O z1 http://localhost:12345/viterbi-seal-rev-770x360.png
- As quickly as you can, type the following in the third window:
wget -O z2 http://localhost:12345/viterbi-seal-rev-770x360.png
- Make sure wget is seeing 1KB/sec (you should try to get within 10% of 1KB/sec, at least in the middle of the downloads when
throughput has stablized).
- Wait for both downloads to complete then do:
diff z1 lab4data/viterbi-seal-rev-770x360.png
diff z2 lab4data/viterbi-seal-rev-770x360.png
The above command should produce no printout. If there is any printout, please fix your server code and try again.
- Press <Ctrl+c> in the first window to kill the server.
- Type "exit" in the first window to close the transcript.
Alternatively, you can also do everything inside one Terminal and run tmux.
You can split the screen vertically into 3 panes and run the server in one pane and run two separate clients one in each other pane.
Part C (lab6c) - controlling the downloading speed of your simple multithreaded web server
(useful for PA3):
In this part of the lab, you will continue with the code you have in Part B of this lab
and control its downloading speed.
In networking in the real world, often times we need to be able to control the speed of data transmissions. This is known as traffic engineering.
This part of the lab is just a starting point and there is very little coding.
In part D of this lab, we will learn how to hit a target download rate accurately.
Please first do the following:
- Copy "lab6b.cpp" (from Part B above) into "lab6c.cpp".
- Modify your Makefile from Part B above
so that when you type "make lab6c" in the commandline, the executable lab6c will be created.
The usage information (i.e., commandline syntax) for running lab6c as a web server is as follows:
lab6c PORT SPEED
where PORT is a TCP port number your server must listen on and SPEED is the target download speed (in the unit of KB/sec)
your multithreaded web server needs to achieve. SPEED must be ≥ 1 and ≤ 100 (i.e., download speed can go
from 1 KB/sec to 100 KB/sec).
In Part B of this lab, after your server have sent an HTTP response header with a status of
200 OK, your server should stay in a loop and read 1,024 bytes
of data at a time, send it to the client, and sleep for one second (by calling usleep(1000000)).
This way, your download speed would be about 1 KB/sec.
In order to achieve a download speed of N KB/sec, one thing you can try is to send 1KB every 1000000/N microseconds
(i.e., by calling usleep(1000000/N)) each time you have sent 1KB of the response body to the client.
This should be pretty straight-forward. The question is whether this will give you the target download speed.
When you are done with implementing lab6c, please do the following:
make clean
make lab6c
./lab6c 12345 10
Run another Terminal windows and cd into the same "lab6" directory.
In the second Terminal window, type "script lab6c10.script" to start a transcript. Then type:
wget -O x http://localhost:12345/viterbi-seal-rev-770x360.png
Wait for download to complete and then do:
diff x lab4data/viterbi-seal-rev-770x360.png
to make sure that your download was successful.
Sometimes, your laptop/desktop may be running slow because it's busy doing something else.
Therefore, if wget did not get within ±10% of 10 KB/s, run the above command two more times.
Don't be surprised if the download speed is off. We will fix it in part D of this lab.
Press <Ctrl+c> in the first window to kill the server.
Type "exit" in the second window to close the transcript.
Type "more lab6c10.script" in the 2nd Terminal window to see if the download speed is within ±10% of 10 KB/s.
Alternatively, you can also do everything inside one Terminal and run tmux.
You can split the screen vertically and run the client and server in separate panes.
Part D (lab6d) - finer control of the downloading speed of your simple multithreaded web server
(useful for PA3):
In Part C of this lab, as the target download speed gets higher,
the actual download speed achieved by wget gets more different from the target speed.
In this part of the lab, you will implement a Token Bucket Filter mechanism described in the PA3 spec.
The description there is a bit tedious. Since we are implementing a special case here (with P=1),
please see the pseudo-code below. Please refer to the PA3 spec
for detailed explanation of the mechanism.
Please first do the following:
- Copy "lab6c.cpp" (from Part C above) into "lab6d.cpp"
- Modify your Makefile from Part C above
so that when you type "make lab6d" in the commandline, the executable lab6d will be created.
The usage information (i.e., commandline syntax) for running lab6d as a web server is as follows:
lab6d PORT SPEED
where PORT is a TCP port number your server must listen on and SPEED is the target download speed (in the unit of KB/sec)
your multithreaded web server needs to achieve. SPEED must be ≥ 1 and ≤ 100 (i.e., download speed can go
from 1 KB/sec to 100 KB/sec).
When you are done with implementing lab6d, please do the following:
make clean
make lab6d
./lab6d 12345 10
Run another Terminal windows and cd into the same "lab6" directory.
In the second Terminal window, type "script lab6d10.script" to start a transcript. Then type:
wget -O x http://localhost:12345/viterbi-seal-rev-770x360.png
diff x lab4data/viterbi-seal-rev-770x360.png
Wait for download to complete.
Press <Ctrl+c> in the first window to kill the server.
Type "exit" in the second window to close the transcript.
Type "more lab6d10.script" in the 2nd Terminal window to see if the download speed is within ±10% of 10 KB/s.
If it's not, you should debug and fix your code.
- In the first Terminal window, type "./lab6d 12345 90".
- In the second Terminal window, type "script lab6d90.script" to start a transcript. Then type:
wget -O x http://localhost:12345/upc_map.pdf
- As quickly as you can, type the following in the third Terminal window:
wget -O y http://localhost:12345/upc_map.pdf
- Wait for both downloads to complete then do:
diff x lab4data/upc_map.pdf
diff y lab4data/upc_map.pdf
- Press <Ctrl+c> in the first window to kill the server.
- Type "exit" in the second window to close the transcript.
- Type "more lab6d90.script" in the 2nd Terminal window to see if the download speed is within ±10% of 90 KB/s.
If it's not, you should debug and fix your code.
Alternatively, you can also do everything inside one Terminal and run tmux.
You can split the screen vertically into 3 panes and run the server in one pane and run two separate clients one in each other pane.
All pseudo-code is incomplete and error checking is often left out in pseudo-code.
Feel free to send your questions (and not your code) to the instructor.
Pseudo-code (not necessarily complete) for lab6d (when ready to send message body of a 200 OK message):
P = 1
r = SPEED /* from argv[2] */
t1 = gettimeofday()
b1 = P
data = read 1024 bytes from file
while (data.size > 0) do
write(socket_fd, data)
not_enough_tokens = true
while (not_enough_tokens) do
t2 = gettimeofday()
n = (int)(r × (t2 - t1)) /* must truncate and not round() */
if ((n > 1) || (b1 == P && b1-P+n ≥ P) || (b1 < P && b1+n ≥ P)) then
t1 = t1 + (1/r)
b1 = P
not_enough_tokens = false
else
/* since P is 1, we must have n == 0 and b1 == 1 here */
b1 = 0 /* b1 = b1-P+n */
t3 = t1 + (1/r) /* t3 = t1 + (P-b1)/r is the time to wake up */
time_to_sleep = t3 - t2
usec_to_sleep = time_to_sleep * 1000000
if (usec_to_sleep > 0) then
usleep(usec_to_sleep)
end-if
end-if
end-while
/* got enough tokens */
data = read 1024 bytes from file
end-while
For a discussion about why the above code would work, please see the old Lab 6 lecture slides.
Please note that variable P in the above pseudo-code is the P in the old Lab 6 lecture slides.
It is not the value of a P key inside a PA3 CONFIGFILE.
Sorry about the confusing notation.
Please download "my_timestamp.cpp" (and "my_timestamp.h")
so you can use timestamp_diff_in_seconds() to compute things like t2-t1 above and
add_seconds_to_timestamp() to compute things like t1+(1/r) above.
Please understand that if you use 1/r in your code, if r is an integer, then 1/r is zero!
So, do (((double)1.0)/((double)r)) instead (I love parentheses and sometimes I over-use them)!
Please also remember, always use double when you need a floating point number and NEVER use the float data type!
Below is the grading breakdown:
- (1 pt) submitted a valid lab6.tar.gz file with all the required files using the submission procedure below
- (1 pt) content in "lab6a.script" and "lab6b.script" are correct
- (1 pt) content in "lab6c10.script", "lab6c30.script", "lab6c50.script", "lab6c70.script", "lab6c90.script",
"lab6d10.script", "lab6d30.script", "lab6d50.script", "lab6d70.script", and "lab6d90.script" are correct
- (1 pt) "Makefile" works for "make lab6b", "make lab6c", and "make lab6d"
- (1 pt) source code of your multithreaded web server programs ("lab6b.cpp", "lab6c.cpp", and "lab6d.cpp") looks right
Minimum deduction is 0.5 pt for anything that's incorrect.
Please note that for the " Makefile" item, you can only get credit for it if your "source code" is relevant to this lab; therefore, you can only get as many points as the "source code" item
in the best case.
Please keep in mind that even though lab grading is "light", it doesn't mean that you can just put anything
into your submission! It's still your responsibility to make sure that the files in your submission contains
information that's relevant to the tests you were supposed to run.
Use the "more" command to view your script/log files to make sure that they contain the right information.
If a file has the wrong stuff in it, you should delete it and create the file again and verify.
If most of the stuff in your script/log files are wrong and you did not notice it, we will most likely have to take points off.
To submit your work, you must first tar all the files you want to submit into a tarball and
gzip it to create a gzipped tarfile named " lab6.tar.gz".
Then you upload " lab6.tar.gz" to our Bistro submission server.
Change into the "lab6" directory you have created above and enter the following command
to create your submission file "lab6.tar.gz" (if you don't have any ".h" files, don't include "*.h*" at the end):
tar cvzf lab6.tar.gz lab6*.script Makefile *.c* *.h*
ls -l lab6.tar.gz
The last command shows you how big the created " lab6.tar.gz" file is.
If " lab6.tar.gz" is larger than 1MB in size, the submission server will not accept it.
If you use an IDE, the IDE may put your source code in subdirectories. In that case,
you need to modify the commands above so that you include ALL
the necessary source files and subdirectories (and don't include any binary files)
ane make sure that your code can be compiled without the IDE since the grader is not allowed to use an IDE to compile your code.
You should read the output of the above commands carefully to make sure that "lab6.tar.gz" is created properly.
If you don't understand the output of the above commands, you need to learn how to read it!
It's your responsibility to ensure that "lab6.tar.gz" is created properly.
To check the content of "lab6.tar.gz", you can use the following command:
tar tvf lab6.tar.gz
Please read the output of the above command carefully to see what files were included in " lab6.tar.gz"
and what are their file sizes and make sure that they make sense.
Please enter your USC e-mail address and your submission PIN below. Then click on the Browse button
and locate and select your submission file (i.e., "lab6.tar.gz").
Then click on the Upload button to submit your "lab6.tar.gz".
(Be careful what you click! Do NOT submit the wrong file!)
If you see an error message, please read the dialogbox carefully and fix what needs to be fixed and repeat the procedure.
If you don't know your submission PIN, please visit this web site to have your PIN e-mailed to your USC e-mail address.
When this web page was last loaded, the time at the submission server at merlot.usc.edu was
27Nov2025-18:59:15.
Reload this web page to see the current time on merlot.usc.edu.
If the command is executed successfully and if everything checks out,
a ticket will be issued to you to let you know "what" and "when"
your submission made it to the Bistro server. The next web page you
see would display such a ticket and the ticket should look like
the sample shown in the submission web page
(of course, the actual text would be different, but the format should be similar).
Make sure you follow the Verify Your Ticket instructions
to verify the SHA1 hash of your submission to make sure what you did not accidentally submit the wrong file.
Also, an e-mail (showing the ticket) will be sent to your USC e-mail address.
Please read the ticket carefully to know exactly "what" and "when"
your submission made it to the Bistro server.
If there are problems, please contact the instructor.
It is extreme important that you also verify your submission
after you have submitted "lab6.tar.gz" electronically to make
sure that every you have submitted is everything you wanted us to grade.
If you don't verify your submission and
you ended up submit the wrong files, please understand that due to our fairness policy,
there's absolutely nothing we can do.
Finally, please be familiar with the Electronic Submission Guidelines
and information on the bsubmit web page.
|