Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

CSCI 4210  Operating Systems

Homework 4 (document version 1.1)

Network Programming and TCP/IP

● This homework is due in Submitty by 11:59PM EST on Wednesday, April 26, 2023

● You can use at most three late days on this assignment

● This homework is to be done individually, so do not share your code with anyone else

● You must use  C for this assignment,  and all submitted code  must successfully compile via gcc with no warning messages when the -Wall (i.e., warn all) compiler option is used; we will also use -Werror, which will treat all warnings as critical errors

● You must use the POSIX thread (Pthread) library by appending the -pthread ag to gcc

● All submitted code  must successfully compile and run on  Submitty,  which uses Ubuntu v20.04.5 LTS and gcc version 9.4.0 (Ubuntu  9 .4 .0-1ubuntu1~20 .04 .1)

Hints and reminders

To succeed in this course, do not rely on program output to show whether your code is correct. And no guesswork! Instead, consistently allocate exactly the number of bytes you need regardless of whether you use static or dynamic memory allocation.

Further, deallocate dynamically allocated memory via free() at the earliest possible point in your code. Consider using valgrind to check for errors with dynamic memory allocation and use. Also close any open le descriptors or FILE pointers as soon as you are done using them.

Another key to success in this course is to always read (and re-read!) the corresponding man pages for library functions, system calls, etc.  To better understand how man pages are organized, check out the man page for man itself!

Homework specications

In this fourth assignment, you will use C to implement a single-process multi-threaded TCP server for the Wordle word game. You will use POSIX threads to implement a TCP server that handles multiple client connections in parallel.

Specifically, your top-level main thread blocks on the accept() system call, listening on the port number specified as a command-line argument. For each connection request received by your server, create a child thread via pthread_create() to handle that specific connection.

Each child thread manages game play for one client and only for one hidden word.  Both during and after game play ends, child threads update a set of global variables.  Note that child threads

are not joined back in to the main thread.

And remember that all threads run within one process.

Wordle game play

To learn how to play this one-player game, visit https://www .nytimes .com/games/wordle.  In brief, a ve-letter word is selected at random, then a player has up to six guesses to guess this hidden word.  For each guess, the player sees which guessed letters are in the correct position (if any), which guessed letters are in the word in an incorrect position (if any), and which guessed letters are not in the word at all.

Note that only valid ve-letter words are allowed as guesses.  Therefore, if a guess is not in the given words le, it does not count as a guess.  In general, expect your server to receive anything, including erroneous data.

Game play stops when the player guesses the word correctly or runs out of guesses. In either case, the server closes the TCP connection, then the corresponding child thread terminates.

Global variables and compilation

Similar to Homework 3, the given hw4-main .c source le contains a short main() function that initializes four global variables, then calls the wordle_server() function, which you must write in your own hw4 .c source le.

Submitty will compile your hw4 .c code as follows:

bash$  gcc  -Wall  -Werror  hw4-main .c  hw4 .c  -pthread

You are required to make use of the four global variables in the given hw4-main .c source le. To do so, declare them as external variables in your hw4 .c code as follows:

extern  int  total_guesses;

extern  int  total_wins;

extern  int  total_losses;

extern  char  **  words;

(v1.1) The rst three global variables shown above count the total number of valid guesses, the total number of games won, and the total number of games lost, respectively.  These totals are accumulated across all active players during game play.

The words array is a dynamically allocated array of character strings representing all of the words actually used in game play.  This array is initially set (in hw4-main .c) to be an array of size 1, with  *words initialized to NULL.  Similar to  argv,  the last entry in this array must always be NULL so that the list of words can be displayed using a simple loop, as shown below.  (Refer to command-line-args .c for an example using this technique.)

for  (  char  **  ptr  =  words  ;  *ptr  ;  ptr++  )

{

printf(  "WORD:  %s\n",  *ptr  );

}

Submitty test cases will check these global variables when your wordle_server() function returns. As with Homework 3, return either EXIT_SUCCESS or EXIT_FAILURE.

Feel free to use additional global variables in your own code.  And since multiple threads will be accessing and changing these global variables, synchronization is required.

Application-layer protocol

The specifications below focus on the application-layer protocol that your server must implement to successfully communicate with multiple clients simultaneously.

Once a connection is accepted, the client sends a five-byte packet containing a guess, e.g., "ready";

the guessed word can be a mix of uppercase and lowercase letters, as case does not matter. The server replies with a eight-byte packet that is formatted as follows:

+-----+-----+-----+-----+-----+-----+-----+-----+

SERVER  REPLY:      |valid|    guesses    |  result                                            |

|guess|  remaining  |                                                         |

+-----+-----+-----+-----+-----+-----+-----+-----+

The valid guess eld is a one-byte char value that is either  'Y' (yes) or  'N' (no).

The guesses remaining eld is a two-byte short value that indicates how many guesses the client has left.  Since a client starts with six guesses, this counts down from ve to zero for each valid guess made.

The result eld is a ve-byte character string that corresponds to the client’s guess. If a guess is not valid, simply send "?????"; for a valid guess, encode the results as follows. Use an uppercase letter to indicate a matching letter in the correct position.  Use a lowercase letter to indicate a letter that is in the word but not in the correct position.  And use a  '- ' character to indicate an incorrect letter not in the word at all.

As an example, if the hidden word is wears,” and a client guesses ready,” the server replies with "rEA-- "; note that words may contain duplicate letters, e.g., “muddy” and radar” and so on.

If the client sends the correct word or the number of guesses remaining is zero, the server closes the TCP connection.

Command-line arguments

There are four required command-line arguments.  The rst command-line argument specifies the TCP listener port number.

The second command-line argument specifies the seed value for the pseudo-random number generator— this is used to randomly” select words in a predictable and repeatable manner via rand().

The third command-line argument specifies the name (or path) of the input le containing valid words, and the fourth command-line argument specifies the number of words in this input le.

Validate the inputs as necessary. If invalid, display the following to stderr and return EXIT_FAILURE:

ERROR:  Invalid  argument(s)

USAGE:  hw4 .out  <listener-port>  <seed>  <word-filename>  <num-words>

The input le should contain words delimited by newline characters.  Case does not matter. Here is an example le:  "ready\nheavy\nUPPER\nVaguE\n"

Signals and server termination

Since servers are typically designed to run forever without interruption, ignore signals  SIGINT, SIGTERM, and SIGUSR2.

Still, we need a mechanism to shut down the server. Set up a signal handler for SIGUSR1 that grace- fully shuts down your server by shutting down any running child threads, freeing up dynamically allocated memory, and returning from the wordle_server() function with EXIT_SUCCESS.

Dynamic memory allocation

As with previous homeworks, you must use calloc() to dynamically allocate memory.  For the global words array, you must also use realloc() to extend the size of the array.

Do not use malloc(). And of course, be sure your program has no memory leaks.

No square brackets allowed!

As we perfect the use of pointers and pointer arithmetic, once again, you are not allowed to use square brackets anywhere in your code!

If a '[' or ']' character is detected, including within comments, that line of code will be removed before running gcc.

Program execution and required output

To illustrate via an example, you could execute your program as shown below ((v1.1) updated). You will have your server process running and listening on port 8192 for incoming TCP connection requests.

bash$  ./hw4 .out  8192  111  wordle-words .txt  5757

MAIN:  opened  wordle-words .txt  (5757  words)

MAIN:  Wordle  server  listening  on  port  8192

MAIN:  rcvd  incoming  connection  request

THREAD  139711842105088:  waiting  for  guess

THREAD  139711842105088:  rcvd  guess:  stare

THREAD  139711842105088:  sending  reply:  --ArE  (5  guesses  left)

THREAD  139711842105088:  waiting  for  guess

THREAD  139711842105088:  rcvd  guess:  brade

THREAD  139711842105088:  invalid  guess;  sending  reply:  ?????  (5  guesses  left)

THREAD  139711842105088:  waiting  for  guess

MAIN:  rcvd  incoming  connection  request

THREAD  139711833601792:  waiting  for  guess

THREAD  139711842105088:  rcvd  guess:  brake

THREAD  139711842105088:  sending  reply:  BRA-E  (4  guesses  left)

THREAD  139711842105088:  waiting  for  guess

THREAD  139711842105088:  rcvd  guess:  brace

THREAD  139711842105088:  sending  reply:  BRACE  (3  guesses  left)

THREAD  139711842105088:  game  over;  word  was  BRACE!

...

MAIN:  SIGUSR1  rcvd;  Wordle  server  shutting  down . . .

To display thread IDs, use  "\%lu" and pthread_self() in your printf() calls.  Note that you might see duplicate thread IDs for threads not running in parallel.

If a client closes the TCP connection,  your server must detect that and display the following ((v1.1) and count this as a loss):

THREAD  139711833601792:  client  gave  up;  closing  TCP  connection . . .

Match the above output format exactly  as  shown  above, though note that thread IDs will certainly vary. Also, interleaving output across multiple child threads is expected, though the rst three lines and the last line must be rst and last, respectively.

Error handling

In general, if an error is encountered in any thread, display a meaningful error message on stderr by  using either perror() or fprintf(), then abort further thread execution by calling pthread_exit().

Only use perror() if the given library function or system call sets the global errno variable. Error messages must be one line only and use the following format:

ERROR:  <error-text-here>

Submission Instructions

To submit your assignment (and also perform nal testing of your code), please use Submitty.

Note that this assignment will be available on Submitty a minimum of three days before the due date.  Please do not ask when Submitty will be available, as you should rst perform adequate testing on your own Ubuntu platform.

That said, to make sure that your program does execute properly everywhere, including Submitty, use the techniques below.

First, make use of the DEBUG_MODE technique to make sure that Submitty does not execute any debugging code. Here is an example:

#ifdef  DEBUG_MODE

printf(  "the  value  of  q  is  %d\n",  q  );

printf(  "here12\n"  );

printf(  "why  is my  program  crashing  here?!\n"  );

printf(  "aaaaaaaaaaaaagggggggghhhh!\n"  );

#endif

And to compile this code in debug” mode, use the -D ag as follows:

bash$  gcc  -Wall  -Werror  -g  -D  DEBUG_MODE  hw4 .c  -pthread

Second, output to standard output (stdout) is buffered. To disable buffered output for grading on Submitty, use setvbuf() as follows:

setvbuf(  stdout,  NULL,  _IONBF,  0  );

You would not generally do this in practice, as this can substantially slow down your program, but to ensure good results on Submitty, this is a good technique to use.