CS 392
Spring 2025
Systems Programming
Project
Preliminary
Concepts you need to understand for this assignment:
r File I/O, both low- and high-level;
r The steps of establishing socket connections;
r Multiplexed I/O.
Your task is to write a Trivia game application as shown in class. The server acts as the host who sends questions to the players, and the clients are the players.
1 Part 1: The Server (70 pts)
1.1 Task 1: Establish the Server with getopt (10 pts)
We will start writing the server first, where the first step is to parse arguments from command line using getopt() . It is very similar to getopts utility we used in bash script.
There are four possible flags, all of which are optional for the program:
1 Usage: ./server [-f question_file] [-i IP_address] [-p port_number] [-h]
2
3 -f question_file Default to "qshort.txt";
4 -i IP_address Default to "127.0.0.1";
5 -p port_number Default to 25555;
6 -h Display this help info .
Above is also an example output of the help message. Note that ./server in the help message must not be hardcoded – it has to be replaced by the actual name of the server program. If an unknown flag is passed, please print the following message and exit with failure:
1 Error: Unknown option '-' received .
where is replaced by the actual unknown flag.
After parsing the arguments, you will follow the steps to create a server socket, bind it, and listen. Please limit the maximal number of connections to 2 or 3 when listening (for your own good s). If listen() succeeds, print the following message:
1 Welcome to 392 Trivia!
1.2 Task 2: Read the Question Database (10 pts)
Once the server has been established, we can create our question database before the game starts. This is accomplished by reading a plain text file, which has the format as follows:
1 Which Game of Thrones character is known as the Young Wolf?
2 Robb_Stark Arya_Stark Sansa_Stark
3 Robb_Stark 4
5 What city hosted the 2000 Olympic Games?
6 Tokyo Beijing Sydney
7 Sydney
For each question entry, the first line is the question itself, and the second line the three possible options, and the third line the answer. Between every two question entries there’s an empty line. Notice that if the answer has a space in it, there’s always an underscore, so a white space is always used to separate three options.
We use the following structure to store each question entry:
1 struct Entry {
2 char prompt[1024];
3 char options[3][50];
4 int answer_idx;
5 };
where prompt is the question itself, options are the three options, and answer_idx is the index to the correct answer in options .
The function that reads the question database is declared as follows:
1 int read_questions(struct Entry* arr, char* filename);
where arr is an array of question entries and filename the path to the question database file. The function returns the actual number of question entries read from the file.
You can assume there’s no more than 50 questions, so a hardcoded struct Entry array of 50 is acceptable.
1.3 Task 3: Accepting Players (20 pts)
Now is the time to accept new players!
When the maximal number of players have been reached, you should print the following message on screen:
1 Max connection reached!
and close the new player’s file descriptor.
If there’s still empty spot for players, print the following message on screen:
1 New connection detected!
and send out the following message to the player:
1 Please type your name:
After you receive the name sent by the player, you will print the following message on screen:
1 Hi !
where is replaced by the name sent from the player. You don’t need to check duplicates; it’s ok to assume each player will put in a different name.
For each player, you will use the following structure to store their information:
1 struct Player {
2 int fd;
3 int score;
4 char name[128];
5 };
where fd is the file descriptor assigned to this player when connected to the server, score the score of this player, and name the player’s name.
Once the last player has typed their name and you received it in the server, you will print out the following message on screen:
1 The game starts now!
If a player quits their program, print the following message on screen:
1 Lost connection!
1.4 Task 4: Start the Game! (30 pts)
The game starts now! You will print one question and the three options to both the screen and each of the player’s terminal.
On the screen, the question and the options are formatted as follows:
1 Question :
2 1:
3 2:
4 3:
where starts from 1.
On each player’s terminal, the question should be formatted as follows:
1 Question :
2 Press 1:
3 Press 2:
4 Press 3:
Once a player pressed one of the numbers, we apply rewards or penalty to the player. The rule is, if a player answered the question right, they get one point; otherwise they get -1. Regardless of the option the player chose, you will display the correct answer on the screen, and also broadcast it to all the players. Then you can move on to the next question.
When all the questions have been answered, display the winner with the following message on the screen:
1 Congrats, !
Then close all the connections, and exit the server program.
2 Part 2: The Client (30 pts)
2.1 Task 5: Parse Arguments and Connect to the Server (10 pts)
The client (player) side is relatively easier. The first task is still to parse the flags, all of which are optional for the program:
1 Usage: ./client [-i IP_address] [-p port_number] [-h]
2
3 -i IP_address Default to "127.0.0.1";
4 -p port_number Default to 25555;
5 -h Display this help info .
Again, ./client in the help message must not be hardcoded – it has to be replaced by the actual name of the client program. If an unknown flag is passed, please print the following message and exit with failure:
1 Error: Unknown option '-' received .
where is replaced by the actual unknown flag.
After parsing the arguments, you will follow the steps to create a socket and connect it to the server. The function is declared as follows:
1 void parse_connect(int argc, char** argv, int* server_fd);
2.2 Task 6: Enter the Game (20 pts)
Once connected to the server, the server will send out the message to let the player type their name (see Part 1, Task 3). The player types their name, and wait for the game to start. During the game, if another player answered the question first, the server will send out the correct answer to all the players, and start the next question. If the current player knows the answer, they should act fast and press 1, 2, or 3, before the correct answer shows up.
After all questions have been asked, the server cuts the connection to all the players, and the player program can exit.
Hint: the client side also needs to implement multiplexed I/O, because after the question is shown to the player, there are two possible ways to move on to the next question: either the current player presses 1, 2, or 3 to answer the question, or another player answers the question first and the server sends out the correct answer. Therefore, you need to monitor two file descriptors to see which one is ready first: stdin , or the server’s file descriptor.
3 Grading
The project will be graded based on a total of 100 points.
r Server:
• Task 1 (10 pts): 10 test cases in total, 2 points each;
• Task 2 (10 pts): 10 test cases in total, 3 points each;
• Task 3 (20 pts): 10 test cases in total, 2 points each;
• Task 4 (30 pts): 10 test cases in total, 3 points each;
r Client:
• Task 5 (10 pts): 10 test cases in total, 3 points each;
• Task 6 (20 pts): 10 test cases in total, 3 points each.
After accumulating points from the testing above, we will inspect your code and apply deductibles listed below. The lowest score is 0, so no negative scores. Note that all the deduction rules are also applied to the helper function you created and called in that task.
r General (only deduct once, applicable to both the server and the client):
• -100: the code does not compile through the Makefile, or executes with run-time error;
• -100: the code is generated by AI, and/or through reverse engineering on the tester;
• -30: memory leak through valgrind (only “definitely lost” category);
• -10: no pledge and/or name in C file.
Earlybird Extra Credit: 2% of extra credit will be given if the homework is finished two days before the deadline. For specific policy, see syllabus.
Deliverable
Submit the following files on Canvas. No need to zip.
(1) Makefile ;
(2) server.c ;
(3) client.c .