Process Management and Distributed Computing
Task 1: Client-Server Computing
You have been commissioned to develop a client/server system for an online games provider
to expand their current offerings to registered clients. The company wishes to offer to their
current clients the game of Hangman, which is a word guessing game.
In the game of Hangman the server randomly selects a two-word phrase from a given text
file. The word phrase is represented by a row of dashes representing each letter of the word
on the client machine. If the client selects a correct letter the letter should then appear in all
the correct places in which it occurs.
The client has a number of turns in which to guess all the letters in the two-word phrase. The
number of turns the client receives is proportional to the length of the two word phrase. If the
client guesses all the letters before running out of turns the client wins the game. If the client
fails to guess all the letters within the number of turns provided the client loses the game.
All the words that are to be used in the game have been provided by the company and they
have insisted that this list is not to be changed. The information stored in this text file is in the
form of object, object type (e.g. beach, place) – each line in the text file represents a pair of
words which are both used in a single game of Hangman.
The client and server will be implemented in the C programming language using BSDsockets on the Linux operating system. The programs (clients and server) are to run in a terminal reading input from thekeyboard and writing output to the screen.
Server
The server will automatically load the hangman_text.txt file which is to be located in the
same directory as the server binary file. The server will take only one command line
parameter that indicates which port the server is to listen on. If no port number is supplied the
default port of 12345 is to be used by the server. The following command will run the server
program on port 12345.
./Server 12345
The server is responsible for ensuring only registered clients of the system can play
Hangman. A file named Authentication.txt contains the names and the passwords of all
registered clients. This file should be located in the same directory as the server binary file.
This file is not to be changed.
The server should not be able to take any input from the terminal once it is running. The only
way that the server is to exit is upon receiving a SIGINT from the operating system (an
interrupt signal). SIGINT is a type of signal found on POSIX systems and in the Linux
environment this means ctrl+c has been pressed at the server terminal. You need to
implement a signal handler and ensure the server exits cleanly. This means the server needs
to deal elegantly with any threads that have been created as well as any open sockets,
dynamically allocated memory and/or open files. When the server receives a SIGINT, it is to
immediately commence the shutdown procedure even if there are currently connected clients.
Client
The client will take two (2) command line parameters: hostname and port number. The
following command will run the client program connecting to the server on port 12345.
./Client server_IP_address 12345
The game of Hangman is only available to registered clients of the company’s online gaming
system. Once the client program starts the client must authenticate by entering a registered
name and password which appear in the Authentication.txt file located on the server. After the
server validates the client’s credentials the client program can proceed to the main menu. If
the client does not authenticate correctly the socket is to be immediately closed.
The client program should be a menu driven application. The client program will have three
options: Play Hangman, Show Leader Board, and Exit.
Authentication.txt.pdf
Username Password
Maolin 111111
Jason 222222
Mike 333333
Peter 444444
Justin 555555
Anna 123123
Timothy 155222
Anthony 123123
Paul 248273
Richie 993844
Solution:
HANGMAN GAME PLAY MODE SERVER – CLIENT
1 How to compile the program
Compile via makefile of Hangman project.
Figure 1‑1: Compile the game
Client: This is client program of hangman game
Server: this is server program of hangman game
2 How to run
2.1 Server side:
./Server
User/Pass using for authenticator when client connect to server store in Authentication.txt.
g: ./Server 12345
Figure 2‑1: Run Server Hangman
2.2 Client side:
./Client
g: ./Client localhost 12345
Figure 2‑2: Run Client Hangman
The server will allow up to 10 clients to use the system at the same time.
3 Play:
Figure 3‑1: Play hangman
Client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include “client_game.h”
#include “client_leaderboard.h”
#define MAXDATASIZE 256 /* max number of bytes we can get at once */
#define ARRAY_SIZE 30
#define RETURNED_ERROR -1
void client_game_loop(int socket_identifier);
int show_menu();
void greeting_message();
//global socket id
int sockfd;
void client_game_loop(int socket_identifier)
{
int socket_id = socket_identifier;
greeting_message();
printf(“Please enter your username–>”);
char* send_buf;
send_buf = calloc(MAXDATASIZE, sizeof(char));
scanf(“%s”, send_buf);
if (send(socket_id, send_buf, MAXDATASIZE, 0) <= 0)
{
return;
}
printf(“Please enter your password–>”);
send_buf = calloc(MAXDATASIZE, sizeof(char));
scanf(“%s”, send_buf);
if (send(socket_id, send_buf, MAXDATASIZE, 0) <= 0)
{
return;
}
//Get the user_id result back from login validation
uint16_t statistics;
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return;
}
int user_id = ntohs(statistics);
if (user_id <= 0)
{
printf(“\nYou entered either an incorrect username or password – disconnection\n”);
close(socket_id);
exit(1);
}
else
{
char username[MAXDATASIZE];
if (recv(socket_id, username, MAXDATASIZE, 0) <= 0)
{
return;
}
printf(“\nWelcome to the Hangman Gaming System\n\n\n”);
int menu_option = 0;
while (menu_option != 3)
{
menu_option = show_menu();
//Send option to server
statistics = htons(menu_option);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return;
}
switch (menu_option)
{
case 1:
if (client_hangman_game(socket_id, username) == RETURNED_ERROR)
{
return;
}
break;
case 2:
if (client_leaderboard(socket_id) == RETURNED_ERROR)
{
return;
}
break;
case 3:
printf(“Quit here\n”);
break;
}
}
}
}
int show_menu()
{
printf(“Please enter a selection\n”);
printf(“<1> Play Hangman\n”);
printf(“<2> Show Leaderboard\n”);
printf(“<3> Quit\n\n”);
int menu_option = 0;
while ((menu_option < 1) || (menu_option > 3))
{
printf(“Selection option 1-3 ->”);
char input;
scanf(“%s”, &input);
menu_option = input – ‘0’;
}
return menu_option;
}
void greeting_message()
{
printf(“============================================\n\n\n”);
printf(“Welcome to the Online Hangman Gaming System\n\n\n”);
printf(“============================================\n\n\n”);
printf(“You are required to login with your registered username and Password \n\n”);
}
//client singal handler for CTRL+C
void handler(int sigtype)
{
close(sockfd);
exit(1);
}
int main(int argc, char *argv[])
{
signal(SIGINT, handler);
struct hostent *he;
struct sockaddr_in their_addr; /* connector’s address information */
if (argc != 3)
{
fprintf(stderr, “usage: client_hostname port_number\n”);
exit(1);
}
if ((he = gethostbyname(argv[1])) == NULL)
{ /* get the host info */
herror(“gethostbyname”);
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(“socket”);
exit(1);
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(atoi(argv[2])); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *) he->h_addr);
bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */
if (connect(sockfd, (struct sockaddr *) &their_addr, sizeof(struct sockaddr)) == -1)
{
perror(“connect”);
exit(1);
}
printf(“Waiting for server response…\n”);
uint16_t statistics;
if (recv(sockfd, &statistics, sizeof(uint16_t), 0) > 0)
{
// Client game loop
client_game_loop(sockfd);
}
printf(“Disconnected\n”);
close(sockfd);
return 0;
}
client_game.c
#include “client_game.h”
int client_hangman_game(int socket_id, char* username)
{
char current_guessed_letters[MAXDATASIZE];
int num_guesses_left;
uint16_t statistics;
char guessed_word1[MAXDATASIZE];
char guessed_word2[MAXDATASIZE];
int isGameOver = 0;
int isWon = 0;
while (1)
{
//recv game over flag
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
isGameOver = ntohs(statistics);
//recv game won flag
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
isWon = ntohs(statistics);
printf(“\n=======================================================\n”);
//recv current guessed letters
if (recv(socket_id, current_guessed_letters, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
printf(“\nGuessed letters: %s”, current_guessed_letters);
//recv number of guessed left
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
num_guesses_left = ntohs(statistics);
printf(“\n\nNumber of guesses left: %d\n”, num_guesses_left);
//recv guessed word 1
if (recv(socket_id, guessed_word1, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
//recv guessed word 2
if (recv(socket_id, guessed_word2, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
display_guessed_word(guessed_word1, guessed_word2);
//User is won the game
if (isWon)
{
printf(“Game over\n”);
printf(“\n\nWell done %s! You won this round of Hangman!\n\n”, username);
break;
}
//Game over
if (isGameOver)
{
printf(“Game Over\n”);
printf(“\n\nBad luck %s! You have run out of guesses. The hangman got you!\n\n”, username);
break;
}
//Ask user to guess
char send_letter;
printf(“Enter your guess – “);
scanf(“%s”, &send_letter);
//send guessed letter to server
if (send(socket_id, &send_letter, sizeof(char), 0) <= 0)
{
return RETURNED_ERROR;
}
}
return 0;
}
void display_guessed_word(char* word1, char* word2)
{
printf(“\nWord: “);
for (int i = 0; i < strlen(word1); i++)
{
printf(“%c “, word1[i]);
}
for (int i = 0; i < strlen(word2); i++)
{
printf(” %c”, word2[i]);
}
printf(“\n\n”);
}
client_game.h
#ifndef CLIENT_GAME_H_
#define CLIENT_GAME_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXDATASIZE 256
#define RETURNED_ERROR -1
int client_hangman_game(int socket_id, char* username);
void display_guessed_word(char* word1, char* word2);
#endif /* CLIENT_GAME_H_ */
client_leaderboard.c
#include “client_leaderboard.h”
int client_leaderboard(int socket_id)
{
uint16_t statistics;
char username[MAXDATASIZE];
int games_played;
int games_won;
int board_users_num;
//recv board users num
recv(socket_id, &statistics, sizeof(uint16_t), 0);
board_users_num = ntohs(statistics);
if (board_users_num > 0)
{
for (int i = 0; i < board_users_num; i++)
{
//recv username
if (recv(socket_id, username, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
//recv games played
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
games_played = ntohs(statistics);
//recv games won
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
games_won = ntohs(statistics);
printf(“\n==================================================\n\n”);
printf(“Player – %s\n”, username);
printf(“Number of games won – %d\n”, games_won);
printf(“Number of games played – %d\n\n”, games_played);
printf(“==================================================\n\n”);
}
}
else
{
printf(“=============================================================================\n\n”);
printf(“There is no information currently stored in the Leader Board. Try again later\n”);
printf(“\n=============================================================================\n\n\n”);
}
return 0;
}
client_leaderboard.h
#ifndef CLIENT_LEADERBOARD_H_
#define CLIENT_LEADERBOARD_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXDATASIZE 256
#define RETURNED_ERROR -1
int client_leaderboard(int socket_id);
#endif /* CLIENT_LEADERBOARD_H_ */
load_file.c
#include “load_file.h”
void read_authentication_file()
{
char *input_line;
char *str1;
char *str2;
FILE *file;
int index = 0;
file = fopen(“Authentication.txt”, “r”);
if (file == NULL)
{
printf(“No authentication.txt file. \n”);
exit(1);
}
else
{
input_line = calloc(64, sizeof(char));
while (fgets(input_line, 64, file) != NULL)
{
str1 = calloc(64, sizeof(char));
str2 = calloc(64, sizeof(char));
sscanf(input_line, “%s%s”, str1, str2);
users[index].username = str1;
users[index].password = str2;
users[index].user_id = index;
index++;
}
}
fclose(file);
free(input_line);
}
void read_hangman_text_file()
{
char *input_line;
char *str1;
char *str2;
FILE *file;
int index = 0;
file = fopen(“hangman_text.txt”, “r”);
if (file == NULL)
{
printf(“NO hangman_text.txt file. \n”);
exit(1);
}
else
{
input_line = calloc(64, sizeof(char));
while (fgets(input_line, 64, file) != NULL)
{
str1 = calloc(64, sizeof(char));
str2 = calloc(64, sizeof(char));
sscanf(input_line, “%[^,],%s”, str1, str2);
hangman[index].word1 = str1;
hangman[index].word2 = str2;
index++;
}
fclose(file);
free(input_line);
}
}
void reset_users(void)
{
for (int i = 0; i < MAX_USERS_NUMBER; i++)
{
users[i].games_won = 0;
users[i].games_played = 0;
users[i].loggedin = 0;
}
}
load_file.h
#ifndef LOAD_FILE_H_
#define LOAD_FILE_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_USERS_NUMBER 11
#define MAX_HANGMEN_WORDS 288
struct authentication
{
char* username;
char* password;
int games_won;
int games_played;
int loggedin;
int user_id;
};
struct hangman_text_file
{
char* word1;
char* word2;
};
typedef struct authentication users_array;
typedef struct hangman_text_file hangman_text;
extern users_array users[MAX_USERS_NUMBER];
extern hangman_text hangman[MAX_HANGMEN_WORDS];
void read_authentication_file(void);
void read_hangman_text_file(void);
void reset_users(void);
#endif /* LOAD_FILE_H_ */
Server.c
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include “load_file.h”
#include “server_game.h”
#define MAXDATASIZE 256 /* max number of bytes we can get at once */
#define BACKLOG 10
#define MAX_USERS_NUMBER 11
#define MAX_HANGMEN_WORDS 288
#define DEFAULT_PORT 12345
#define NUM_HANDLER_THREADS 10
pthread_mutex_t request_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
pthread_cond_t got_request = PTHREAD_COND_INITIALIZER;
int num_requests = 0;
struct request
{
int socket_id;
struct request* next;
};
struct request* requests_head = NULL;
struct request* last_request = NULL;
//GLOBAL THREADS
pthread_t p_threads[NUM_HANDLER_THREADS];
int thr_id[NUM_HANDLER_THREADS];
int connected_sockets[NUM_HANDLER_THREADS];
//listen socket
int sockfd;
void add_request(int request_num, pthread_mutex_t* p_mutex, pthread_cond_t* p_cond_var);
struct request* get_request(pthread_mutex_t* p_mutex);
void* handle_requests_loop(void* data);
int validate_user(char* name, char* password);
void add_request(int socket_id, pthread_mutex_t* p_mutex, pthread_cond_t* p_cond_var)
{
int rc; /* return code of pthreads functions. */
struct request* a_request; /* pointer to newly added request. */
/* create structure with new request */
a_request = (struct request*) malloc(sizeof(struct request));
if (!a_request)
{ /* malloc failed?? */
fprintf(stderr, “add_request: out of memory\n”);
exit(1);
}
a_request->socket_id = socket_id;
a_request->next = NULL;
/* lock the mutex, to assure exclusive access to the list */
rc = pthread_mutex_lock(p_mutex);
if (rc)
{
printf(“Can’t lock the mutex p_mutex \n”);
exit(1);
}
/* add new request to the end of the list, updating list */
/* pointers as required */
if (num_requests == 0)
{ /* special case – list is empty */
requests_head = a_request;
last_request = a_request;
}
else
{
last_request->next = a_request;
last_request = a_request;
}
/* increase total number of pending requests by one. */
num_requests++;
/* unlock mutex */
rc = pthread_mutex_unlock(p_mutex);
/* signal the condition variable – there’s a new request to handle */
rc = pthread_cond_signal(p_cond_var);
}
struct request* get_request(pthread_mutex_t* p_mutex)
{
int rc; /* return code of pthreads functions. */
struct request* a_request; /* pointer to request. */
/* lock the mutex, to assure exclusive access to the list */
rc = pthread_mutex_lock(p_mutex);
if (rc)
{
printf(“Can’t lock the mutex p_mutex \n”);
exit(1);
}
if (num_requests > 0)
{
a_request = requests_head;
requests_head = a_request->next;
if (requests_head == NULL)
{ /* this was the last request on the list */
last_request = NULL;
}
/* decrease the total number of pending requests */
num_requests–;
}
else
{ /* requests list is empty */
a_request = NULL;
}
/* unlock mutex */
rc = pthread_mutex_unlock(p_mutex);
if (rc)
{
printf(“Can’t unlock the mutex p_mutex \n”);
exit(1);
}
/* return the request to the caller. */
return a_request;
}
void* handle_requests_loop(void* data)
{
int rc; /* return code of pthreads functions. */
struct request* a_request; /* pointer to a request. */
int thread_id = *((int *) data);
uint16_t statistics;
/* lock the mutex, to access the requests list exclusively. */
rc = pthread_mutex_lock(&request_mutex);
if (rc)
{
printf(“Can’t lock the mutex request_mutex \n”);
exit(1);
}
/* do forever…. */
while (1)
{
if (num_requests > 0)
{ /* a request is pending */
a_request = get_request(&request_mutex);
if (a_request)
{ /* got a request – handle it and free it */
/* unlock mutex – so other threads would be able to handle */
/* other reqeusts waiting in the queue paralelly. */
rc = pthread_mutex_unlock(&request_mutex);
//get socket id from request
int socket_id = a_request->socket_id;
free(a_request);
//put the socket which is handling by this thread
//into the connect_socket array
connected_sockets[thread_id] = socket_id;
printf(“Thread %d is handle a request\n”, thread_id);
statistics = htons(thread_id);
rc = send(socket_id, &statistics, sizeof(uint16_t), 0);
if (rc > 0)
{
server_game_loop(socket_id);
}
close(connected_sockets[thread_id]);
printf(“Thread %d is finished\n”, thread_id);
/* and lock the mutex again. */
rc = pthread_mutex_lock(&request_mutex);
if (rc)
{
printf(“Can’t lock the mutex request_mutex \n”);
exit(1);
}
}
}
else
{
/* wait for a request to arrive. note the mutex will be */
/* unlocked here, thus allowing other threads access to */
/* requests list. */
rc = pthread_cond_wait(&got_request, &request_mutex);
/* and after we return from pthread_cond_wait, the mutex */
/* is locked again, so we don’t need to lock it ourselves */
}
}
}
void handler(int sigtype)
{
struct request* r_head = requests_head;
printf(“\n===============Caught a signal===================\n”);
printf(“Shutdown Server…\n”);
//Kill all the threads
for (int i = 0; i < NUM_HANDLER_THREADS; i++)
{
close(connected_sockets[i]);
pthread_cancel(p_threads[i]);
}
//Close all the socket in the request list
while (r_head)
{
close(r_head->socket_id);
r_head = r_head->next;
}
//Close the listen socket
close(sockfd);
//exit the server program
exit(1);
}
int main(int argc, char *argv[])
{
signal(SIGINT, handler);
pthread_attr_t attr;
pthread_attr_init(&attr);
int new_fd; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector’s address information */
socklen_t sin_size;
/* Get port number for server to listen on */
if (argc > 2)
{
fprintf(stderr, “usage: client port_number\n”);
exit(1);
}
int port_num;
if (argc == 1)
{
port_num = DEFAULT_PORT;
}
else
{
port_num = atoi(argv[1]);
}
//File IO
read_authentication_file();
read_hangman_text_file();
//initialse users
reset_users();
/* generate the socket */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(“socket”);
exit(1);
}
// Set the socket value to SO_REUSEADDR so that
// the socket can be use after the server restart immediately
int reuseaddr = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) == -1)
{
perror(“setsockopt”);
}
/* generate the end point */
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(port_num); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
{
perror(“bind”);
exit(1);
}
/* start listnening */
if (listen(sockfd, BACKLOG) == -1)
{
perror(“listen”);
exit(1);
}
for (int i = 0; i < NUM_HANDLER_THREADS; i++)
{
thr_id[i] = i;
pthread_create(&p_threads[i], &attr, handle_requests_loop, (void*) &thr_id[i]);
}
printf(“Server starts listnening …\n”);
/* repeat: accept, send, close the connection */
/* for every accepted connection, use a sepetate process or thread to serve it */
while (1)
{ /* main accept() loop */
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *) &their_addr, &sin_size)) == -1)
{
perror(“accept”);
continue;
}
printf(“server: got connection from %s\n”, inet_ntoa(their_addr.sin_addr));
add_request(new_fd, &request_mutex, &got_request);
}
}
server_game.c
#include “server_game.h”
pthread_mutex_t users_mutex = PTHREAD_MUTEX_INITIALIZER;
int server_hangman_game(int socket_id, int user_id)
{
//get the random words
srand(time(NULL));
int r = rand() % 288;
uint16_t statistics;
printf(“Random %d \n”, r);
pthread_mutex_lock(&users_mutex);
char* first_word = hangman
[r]
.word1;
char* second_word = hangman
[r]
.word2;
pthread_mutex_unlock(&users_mutex);
printf(“Word1 is %s and word2 is %s \n”, first_word, second_word);
printf(“Word1 length is %zu and word2 length is %zu \n”, strlen(first_word), strlen(second_word));
//initialize client guessed letters
char* client_guessed_letters = calloc(MAXDATASIZE, sizeof(char));
//number of guesses left
int num_guesses = guesses_min(strlen(first_word) + strlen(second_word) + 10);
printf(“Number of guesses = %d \n”, num_guesses);
//initialise client guessed words
char client_guessed_word1[MAXDATASIZE];
char client_guessed_word2[MAXDATASIZE];
for (int i = 0; i < strlen(first_word); i++)
{
client_guessed_word1[i] = 95;
}
client_guessed_word1[strlen(first_word)] = ‘\0’;
for (int i = 0; i < strlen(second_word); i++)
{
client_guessed_word2[i] = 95;
}
client_guessed_word2[strlen(second_word)] = ‘\0’;
printf(“Client guessed word1 %s, word2 %s\n”, client_guessed_word1, client_guessed_word2);
//initialize client guessed single letter
char recv_letter;
int isGameOver = 0;
int isWon = 0;
while (1)
{
//gameover becuase number of guesses reach 0
if (num_guesses <= 0)
{
isGameOver = 1;
}
//check if two words are the same as client guessed words
if (strcmp(first_word, client_guessed_word1) == 0 && strcmp(second_word, client_guessed_word2) == 0)
{
isWon = 1;
}
//send game status isgameover
statistics = htons(isGameOver);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
statistics = htons(isWon);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
//send guessed letterd
if (send(socket_id, client_guessed_letters, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
//send number of guesses
statistics = htons(num_guesses);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
//send guessed word
if (send(socket_id, client_guessed_word1, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
if (send(socket_id, client_guessed_word2, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
//Player won the game
if (isWon)
{
printf(“\nPlayer %s win the game!\n”, users[user_id].username);
//Save the record to the leader board
pthread_mutex_lock(&users_mutex);
users[user_id].games_won++;
pthread_mutex_unlock(&users_mutex);
printf(“\nPlayer: %s , Games played : %d, Games won:%d \n”, users[user_id].username, users[user_id].games_played, users[user_id].games_won);
break;
}
//Game over
if (isGameOver)
{
printf(“\nPlayer %s lose the game!\n”, users[user_id].username);
printf(“\nPlayer: %s , Games played : %d, Games won:%d \n”, users[user_id].username, users[user_id].games_played, users[user_id].games_won);
break;
}
//receive next guess letter
if (recv(socket_id, &recv_letter, sizeof(char), 0) <= 0)
{
return RETURNED_ERROR;
}
//decrese number of guess by 1
num_guesses–;
//add the receive letter to the guesses letter if not exist
if (strchr(client_guessed_letters, recv_letter) == NULL)
{
strcat(client_guessed_letters, &recv_letter);
}
//put the receive letter into guessed words if the letter is right
for (int i = 0; i < strlen(first_word); i++)
{
if (first_word[i] == recv_letter)
{
client_guessed_word1[i] = first_word[i];
}
}
for (int i = 0; i < strlen(second_word); i++)
{
if (second_word[i] == recv_letter)
{
client_guessed_word2[i] = second_word[i];
}
}
}
return 0;
}
void server_game_loop(int socket_id)
{
char username[MAXDATASIZE];
char password[MAXDATASIZE];
if (recv(socket_id, username, MAXDATASIZE, 0) <= 0)
{
return;
}
printf(“Username : %s \n”, username);
if (recv(socket_id, password, MAXDATASIZE, 0) <= 0)
{
return;
}
printf(“Password : %s \n”, password);
int user_id = validate_user(username, password);
uint16_t statistics;
statistics = htons(user_id);
if (user_id <= 0)
{
printf(“User login fail \n”);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return;
}
}
else
{
//send user_id to client
printf(“User id : %d %s login successfully\n”, user_id, users[user_id].username);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return;
}
if (send(socket_id, users[user_id].username, MAXDATASIZE, 0) <= 0)
{
return;
}
int menu_option = 0;
while (menu_option != 3)
{
//receive menu option
if (recv(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return;
}
menu_option = ntohs(statistics);
printf(“User menu option : %d \n”, menu_option);
//Check menu option in server
switch (menu_option)
{
case 1:
printf(“User %s just started Hangman game\n”, users[user_id].username);
//mutex
pthread_mutex_lock(&users_mutex);
users[user_id].games_played++;
pthread_mutex_unlock(&users_mutex);
if (server_hangman_game(socket_id, user_id) == RETURNED_ERROR)
{
return;
}
break;
case 2:
printf(“User %s open Leaderboard\n”, users[user_id].username);
if (server_leaderboard(socket_id) == RETURNED_ERROR)
{
return;
}
break;
case 3:
printf(“User %s Quit\n”, users[user_id].username);
break;
}
}
}
}
int validate_user(char* name, char* password)
{
int i;
for (i = 1; i < MAX_USERS_NUMBER; i++)
{
if (strcmp(name, users[i].username) == 0)
{
if (strcmp(password, users[i].password) == 0)
{
return i;
}
else
{
return 0;
}
}
}
return 0;
}
int guesses_min(int words_length)
{
if (words_length <= 26)
{
return words_length;
}
else
{
return 26;
}
}
server_game.h
#ifndef SERVER_GAME_H_
#define SERVER_GAME_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include “pthread.h”
#include “load_file.h”
#include “server_leaderboard.h”
#define MAXDATASIZE 256
#define MAX_USERS_NUMBER 11
#define RETURNED_ERROR -1
extern pthread_mutex_t users_mutex;
//GLobal variables
users_array users[MAX_USERS_NUMBER];
hangman_text hangman[MAX_HANGMEN_WORDS];
int server_hangman_game(int socket_id, int user_id);
int guesses_min(int words_length);
int validate_user(char* name, char* password);
void server_game_loop(int socket_id);
#endif /* SERVER_GAME_H_ */
server_leaderboard.c
#include “server_leaderboard.h”
pthread_mutex_t board_mutex = PTHREAD_MUTEX_INITIALIZER;
int server_leaderboard(int socket_id)
{
//load user to a board linked list
load_leaderboard();
//sort the users
sort_leaderboard();
//print users for debugging
print_leaderboard();
//send leaderboard to client
if (send_leaderboard(socket_id) == RETURNED_ERROR)
{
pthread_mutex_unlock(&board_mutex);
return RETURNED_ERROR;
}
return 0;
}
int send_leaderboard(int socket_id)
{
uint16_t statistics;
pthread_mutex_lock(&board_mutex);
statistics = htons(board_users_num);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
if (board_users_num > 0)
{
for (int i = 0; i < board_users_num; i++)
{
//send username
if (send(socket_id, board[i].username, MAXDATASIZE, 0) <= 0)
{
return RETURNED_ERROR;
}
//send games played
statistics = htons(board[i].games_played);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
//send games won
statistics = htons(board[i].games_won);
if (send(socket_id, &statistics, sizeof(uint16_t), 0) <= 0)
{
return RETURNED_ERROR;
}
}
}
pthread_mutex_unlock(&board_mutex);
return 0;
}
void load_leaderboard()
{
int index = 0;
pthread_mutex_lock(&board_mutex);
pthread_mutex_lock(&users_mutex);
for (int i = 1; i < MAX_USERS_NUMBER; i++)
{
if (users[i].games_played > 0)
{
board[index].username = users[i].username;
board[index].games_played = users[i].games_played;
board[index].games_won = users[i].games_won;
index++;
}
}
board_users_num = index;
pthread_mutex_unlock(&users_mutex);
pthread_mutex_unlock(&board_mutex);
}
void sort_leaderboard()
{
leaderboard_node tmp;
pthread_mutex_lock(&board_mutex);
for (int i = 0; i < board_users_num; ++i)
{
for (int j = i + 1; j < board_users_num; ++j)
{
if (board[i].games_won > board[j].games_won)
{
tmp = board[j];
board[j] = board[i];
board[i] = tmp;
}
else if (board[i].games_won == board[j].games_won)
{
double win_per1 = board[i].games_won / board[i].games_played;
double win_per2 = board[j].games_won / board[j].games_played;
if (win_per1 > win_per2)
{
tmp = board[j];
board[j] = board[i];
board[i] = tmp;
}
else if (win_per1 == win_per2)
{
if (strcmp(board[i].username, board[j].username) > 0)
{
tmp = board[j];
board[j] = board[i];
board[i] = tmp;
}
}
}
}
}
pthread_mutex_unlock(&board_mutex);
}
void print_leaderboard()
{
pthread_mutex_lock(&board_mutex);
printf(“========Leaderboard==========\n”);
for (int i = 0; i < board_users_num; i++)
{
printf(“User: %s, Game played: %d, Game won: %d\n”, board[i].username, board[i].games_played, board[i].games_won);
}
pthread_mutex_unlock(&board_mutex);
}
server_leaderboard.h
#ifndef SERVER_LEADERBOARD_H_
#define SERVER_LEADERBOARD_H_
#define MAXDATASIZE 256
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include “load_file.h”
#include “server_game.h”
#define MAX_USERS_NUMBER 11
#define RETURNED_ERROR -1
struct leader_board
{
char* username;
int games_won;
int games_played;
int user_id;
};
typedef struct leader_board leaderboard_node;
leaderboard_node board[10];
int board_users_num;
int server_leaderboard(int socket_id);
//Sort the leaderboard
void sort_leaderboard();
//Load the leaderboard from the users array
void load_leaderboard();
//For debugging on server
void print_leaderboard();
//Send the leaderboard to the client
int send_leaderboard(int socket_id);
#endif /* SERVER_LEADERBOARD_H_ */