Lab 10 - Socket Programming: Multiple Clients
Goals for this lab
- Apply your understanding of multi-threading and synchronization to client-server sockets.
- Understand a parallel flow of information to a server.
- Able to stop a thread.
Prerequisite Skills for This Lab
- Linux socket programming.
- C Skills
- Able to use structs, arrays, pointers
- Able to compile, run, and debug C programs using CMake.
Task 1: Download & Compile
- Clone the Lab 10 starter project
- Once you have the files, you are welcome to copy them elsewhere, such as your course-work repo.
- Once you have the files, you are welcome to copy them elsewhere, such as your course-work repo.
- Compile and run the program. Use multiple terminals:
- One for
nvim
: I suggest opening all .c files at once (nvim *.c
). You can switch between them using the commands:b1
and:b2
. - One for running the server: use
build_and_run_server.sh
. - One for running multiple clients: use
build_and_run_clients.sh
. - You only need to run
cmake
once to generate the makefiles in/build
, assuming you don't create additional .c files. - Each time you edit
server.c
orclient.c
, re-run the appropriatebuild_and_run_xxxxxx.sh
script.
- One for
- Copy-and-paste the following questions into the top of the provided
server.c
, and then answer them. You can run the code and read the code to discover the answers./* Questions to answer at top of server.c: (You should not need to change client.c) Understanding the Client: 1. How is the client sending data to the server? What protocol? 2. What data is the client sending to the server? Understanding the Server: 1. Explain the argument that the `run_acceptor` thread is passed as an argument. 2. How are received messages stored? 3. What does `main()` do with the received messages? 4. How are threads used in this sample code? */
Task 2: Server Implementation
Complete the implementation for the server (server.c
) so that..
- In server.c's
main()
function, find the// TODO
for waiting until enough messages are received.- Add code to wait until the number of messages received is at least
MAX_CLIENTS * NUM_MSG_PER_CLIENT
. - OK to use a busy wait here if you like (loop that continuously re-checks it).
- Be careful about critical sections. You'll need to use a mutex to safely read the number of messages.
- Add code to wait until the number of messages received is at least
- In server.c's
run_acceptor()
, find the// TODO
for creating the threads.- Add new code to launch a new thread for each connected client.
- Hint: All necessary parameters to
pthread_create()
are created and ready to pass in. - Remember to count the number of clients connected (
num_clients
). - When you accept a new connection, print "Client connected!\n" to the screen.
- In server.c's
run_client()
, find the// TODO
.- Call
add_to_list()
, passing in the correct arguments to add the newly created node to the list. - Hint: All parameters are readily available to just be passed in.
- Ensure you are writing thread-safe code. You'll need to lock something before calling
add_to_list()
.
- Call
- Have your program cleanly shut-down threads:
- Find the TODOs in
run_acceptor()
. - To set the stop flag, you are looking for the
run
field in the clients. - You'll need to cleanup the thread and the socket.
- Hint: use
pthread_join()
andclose()
respectively.
- Find the TODOs in
- Answer this question in the top of your server.c:
- Explain the use of non-blocking sockets in this lab.
- How are sockets made non-blocking?
- What sockets are made non-blocking?
- Why are these sockets made non-blocking? What purpose does it serve?
- Explain the use of non-blocking sockets in this lab.
- Output Sample
- Server side with 3 clients connecting:
Client connected! Client connected! Client connected! Client connected! Not accepting any more clients! Collected: Hello Collected: Apple Collected: Car Collected: Green Collected: Dog Collected: Hello Collected: Hello Collected: Hello Collected: Apple Collected: Apple Collected: Apple Collected: Car Collected: Car Collected: Car Collected: Green Collected: Green Collected: Green Collected: Dog Collected: Dog Collected: Dog Collected: 20 All messages were collected!
- Running 3 clients at once.
labs/lab10❯ ./build_and_run_clients.sh [ 33%] Built target server [ 66%] Built target client [100%] Built target server_sol Sent: Hello Sent: Hello Sent: Hello Sent: Hello Sent: Apple Sent: Apple Sent: Apple Sent: Apple Sent: Car Sent: Car Sent: Car Sent: Car Sent: Green Sent: Green Sent: Green Sent: Green Sent: Dog Sent: Dog Sent: Dog Sent: Dog
- Server side with 3 clients connecting:
3. Optional Challenges
- Optional: Lots of clients
- Launch many many clients to try and connect to the server. How good is performance as you scale the number of clients? How can you measure this automatically?
- Optional: Switch to using an atomic integer (
#include <stdatomic.h>
) for the count of number of messages received. - Optional: Switch to using a condition variable to track number of messages received.
Submission
Create a new file name lab10.c
which contains the code from:
client.c
- followed by the code from
server.c
.
Submit your lab10.c
C code to CourSys; the file name must be an exact match to what CourSys is expecting, otherwise it won't accept it.
Submissions will be marked for completion. It must be valid C code that runs (however we are unlikely to actually compile and run the code). You do not need to complete any optional steps.