diff --git a/server.c b/server.c deleted file mode 100644 index 5191c5a95b853b75caaaf8f25d8c3f291040fee2..0000000000000000000000000000000000000000 --- a/server.c +++ /dev/null @@ -1,572 +0,0 @@ -/* -** http-server.c -*/ - -#include <errno.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <arpa/inet.h> -#include <fcntl.h> -#include <netdb.h> -#include <netinet/in.h> -#include <strings.h> -#include <sys/select.h> -#include <sys/sendfile.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - - -// constants -static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\ -Content-Type: text/html\r\n\ -Content-Length: %ld\r\n\r\n"; -static char const * const HTTP_400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"; -static int const HTTP_400_LENGTH = 47; -static char const * const HTTP_404 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"; -static int const HTTP_404_LENGTH = 45; -static int GAMEOVER_STATE = 7; -static int READY_STATE = 3; -static int ENDGAME_STATE = 6; -static int MAX_LENGTH = 20; - -// represents the types of method -typedef enum -{ - GET, - POST, - UNKNOWN -} METHOD; - -// Structure for player -typedef struct Player { - char player_name[20]; - int player_socket; - int count; - char keyword_list[20][20]; - int player_status; -}Player; - -Player* current_player(int sockfd,Player *player1, Player *player2); - -static bool handle_http_request(int sockfd,Player *player1, Player *player2) -{ - // try to read the request - char buff[2049]; - char keyword[20]; - char* starting_keyword; - char* ending_keyword; - bool end = false; - int n = read(sockfd, buff, 2049); - if (n <= 0) - { - if (n < 0) - perror("read"); - else - printf("socket %d close the connection\n", sockfd); - return false; - } - - // terminate the string - buff[n] = 0; - - char * curr = buff; - - // parse the method - METHOD method = UNKNOWN; - if (strncmp(curr, "GET ", 4) == 0) - { - curr += 4; - method = GET; - } - else if (strncmp(curr, "POST ", 5) == 0) - { - curr += 5; - method = POST; - } - else if (write(sockfd, HTTP_400, HTTP_400_LENGTH) < 0) - { - perror("write"); - return false; - } - - // sanitise the URI - while (*curr == '.' || *curr == '/') - ++curr; - // if player quit or the other player quited - if((strstr(buff,"quit=Quit"))||player1->player_status == GAMEOVER_STATE ||player2->player_status == GAMEOVER_STATE){ - // send quit page - struct stat st; - stat("7_gameover.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("7_gameover.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - close(filefd); - // change player status to 7(quit state) - if(player1->player_socket==sockfd){ - player1->player_status=GAMEOVER_STATE; - }else if(player2->player_socket == sockfd){ - player2->player_status=GAMEOVER_STATE; - } - - // reset player struct (reset game) - if(player1->player_status == GAMEOVER_STATE &&player2->player_status ==GAMEOVER_STATE){ - player1->player_status = 0; - strcpy(player1->player_name,"none"); - player1->player_socket = 0; - player1->count = 0; - player2->player_status = 0; - strcpy(player2->player_name,"none"); - player2->player_socket = 0; - player2->count = 0; - } - } - // if player guessing - else if(strstr(buff,"&guess=Guess")){ - // if both player are ready - if(player1->player_status == READY_STATE && player2->player_status == READY_STATE){ - // reset keyword - memset(keyword, 0, sizeof(char)*20); - // find length of keyword and copy keyword using strncpy - starting_keyword = strstr(buff,"keyword="); - ending_keyword = strstr(starting_keyword, "&guess=Guess"); - int keyword_length = ending_keyword-starting_keyword-8; - strncpy(keyword,strstr(buff,"keyword=")+8,keyword_length); - - // find current player - if(player1->player_socket==sockfd){ - // place keyword into player keyword_list - strcpy(player1->keyword_list[player1->count],keyword); - player1->count++; - // check if keyword appeared in other player keyword_list - for(int k=0; k<=player2->count;k++){ - // if keyword appeared in other player keyword_list change both player state to victory state(6) - if(strcmp(keyword,player2->keyword_list[k])==0){ - player1->player_status = ENDGAME_STATE; - player2->player_status = ENDGAME_STATE; - } - } - } - else if(player2->player_socket == sockfd){ - // place keyword into player keyword_list - strcpy(player2->keyword_list[player2->count],keyword); - player2->count++; - // check if keyword appeared in other player keyword_list - for(int k=0; k<=player1->count;k++){ - // if keyword appeared in other player keyword_list change both player state to endgame state(6) - if(strcmp(keyword,player1->keyword_list[k])==0){ - player1->player_status = ENDGAME_STATE; - player2->player_status = ENDGAME_STATE; - } - } - } - // if either player in endgame state - if (player2->player_status == ENDGAME_STATE||player1->player_status == ENDGAME_STATE) { - //reset players keyword_list - player1->count = 0; - player2->count = 0; - memset(player1->keyword_list,0,sizeof(player1->keyword_list [0][0])* 20* 20); - memset(player2->keyword_list,0,sizeof(player2->keyword_list [0][0])* 20 * 20); - - // send endgame page - struct stat st; - stat("6_endgame.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("6_endgame.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - close(filefd); - // if player keyword not match(not victory state) - }else{ - - // send accepted file - struct stat st; - stat("4_accepted.html", &st); - char all_keyword[500]; - long current_length=0; - - // copy keywords in keyword_list to all_keyword string - // calculate the length of extra characters - for(int j=0;j<current_player(sockfd,player1,player2)->count;j++){ - strcpy(all_keyword+current_length,current_player(sockfd,player1,player2)->keyword_list[j]); - current_length += strlen(current_player(sockfd,player1,player2)->keyword_list[j]); - strcpy(all_keyword+current_length,","); - current_length += 1; - strcpy(all_keyword+current_length," "); - current_length += 1; - } - - // add size - long size = st.st_size + current_length; - n = sprintf(buff, HTTP_200_FORMAT, size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // read the content of the HTML file - int filefd = open("4_accepted.html", O_RDONLY); - n = read(filefd, buff, 2048); - if (n < 0) - { - perror("read"); - close(filefd); - return false; - } - close(filefd); - // print all_keyword - // replace behind with body and html - strncpy(strstr(buff,"</body>"), all_keyword,current_length); - strncpy(strstr(buff,all_keyword)+current_length,"</body>\n</html>",13); - - if (write(sockfd, buff, size) < 0) - { - perror("write"); - return false; - } - } - // check if player in victory state (for player after the other player win) - }else if(current_player(sockfd,player1,player2)->player_status == ENDGAME_STATE){ - // send endgame page - struct stat st; - stat("6_endgame.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("6_endgame.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - close(filefd); - // if other player not ready not in ready state(3) - }else{ - // send discarded page - struct stat st; - stat("5_discarded.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("5_discarded.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - } - } - // request intro page - else if (*curr == ' '){ - if (method == GET) - { - //send intro page - struct stat st; - stat("1_intro.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("1_intro.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - close(filefd); - } - else if (method == POST) - { - - // locate username - char * username = strstr(buff, "user=") + 5; - char username_copy[20]; - // ensure username is empty - memset(username_copy, 0, sizeof(char)*MAX_LENGTH); - // copy username - strcpy(username_copy,username); - int username_length = strlen(username); - long added_length = username_length; - - //set player name and socket - if(!strcmp(player1->player_name, "none")){ - strcpy(player1->player_name,username); - player1->player_socket= sockfd; - player1->player_status = 0; - }else if(!strcmp(player2->player_name, "none")){ - strcpy(player2->player_name,username); - player2->player_socket = sockfd; - player2->player_status = 0; - } - // get the size of the file - struct stat st; - stat("2_start.html", &st); - // increase file size to accommodate the username - long size = st.st_size + added_length; - n = sprintf(buff, HTTP_200_FORMAT, size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // read the content of the HTML file - int filefd = open("2_start.html", O_RDONLY); - n = read(filefd, buff, 2048); - if (n < 0) - { - perror("read"); - close(filefd); - return false; - } - close(filefd); - // move the trailing part backward - // copy the username - strncpy(strstr(buff,"</body>"), username_copy, username_length); - strncpy(strstr(buff,username_copy)+username_length,"</body>\n</html>",13); - - - if (write(sockfd, buff, size) < 0) - { - perror("write"); - return false; - } - } - // request for start (start button pressed) - }else if(strstr(buff,"?start=Start")){ - if (method == GET) - { - // change player state to read state (3) - if(player1->player_socket==sockfd){ - player1->player_status= READY_STATE; - }else if(player2->player_socket == sockfd){ - player2->player_status= READY_STATE; - } - // get the size of the file - struct stat st; - stat("3_first_turn.html", &st); - n = sprintf(buff, HTTP_200_FORMAT, st.st_size); - // send the header first - if (write(sockfd, buff, n) < 0) - { - perror("write"); - return false; - } - // send the file - int filefd = open("3_first_turn.html", O_RDONLY); - do - { - n = sendfile(sockfd, filefd, NULL, 2048); - } - while (n > 0); - if (n < 0) - { - perror("sendfile"); - close(filefd); - return false; - } - close(filefd); - }else{ - // never used, just for completeness - fprintf(stderr, "no other methods supported"); - } - // send 404 - } - else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0) - { - perror("write"); - return false; - } - - return true; -} - -int main(int argc, char * argv[]) -{ - if (argc < 3) - { - fprintf(stderr, "usage: %s ip port\n", argv[0]); - return 0; - } - - // create TCP socket which only accept IPv4 - int sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) - { - perror("socket"); - exit(EXIT_FAILURE); - } - - // reuse the socket if possible - int const reuse = 1; - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) - { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - - // create and initialise address we will listen on - struct sockaddr_in serv_addr; - bzero(&serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - // if ip parameter is not specified - serv_addr.sin_addr.s_addr = inet_addr(argv[1]); - serv_addr.sin_port = htons(atoi(argv[2])); - - //create 2 players - Player player1; - strcpy(player1.player_name,"none"); - player1.count = 0; - Player player2; - strcpy(player2.player_name,"none"); - player2.count = 0; - - // bind address to socket - if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) - { - perror("bind"); - exit(EXIT_FAILURE); - } - - // listen on the socket - listen(sockfd, 5); - - // initialise an active file descriptors set - fd_set masterfds; - FD_ZERO(&masterfds); - FD_SET(sockfd, &masterfds); - // record the maximum socket number - int maxfd = sockfd; - - while (1) - { - // monitor file descriptors - fd_set readfds = masterfds; - if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0) - { - perror("select"); - exit(EXIT_FAILURE); - } - - // loop all possible descriptor - for (int i = 0; i <= maxfd; ++i) - // determine if the current file descriptor is active - if (FD_ISSET(i, &readfds)) - { - // create new socket if there is new incoming connection request - if (i == sockfd) - { - struct sockaddr_in cliaddr; - socklen_t clilen = sizeof(cliaddr); - int newsockfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen); - if (newsockfd < 0) - perror("accept"); - else - { - // add the socket to the set - FD_SET(newsockfd, &masterfds); - // update the maximum tracker - if (newsockfd > maxfd) - maxfd = newsockfd; - // print out the IP and the socket number - char ip[INET_ADDRSTRLEN]; - printf( - "new connection from %s on socket %d\n", - // convert to human readable string - inet_ntop(cliaddr.sin_family, &cliaddr.sin_addr, ip, INET_ADDRSTRLEN), - newsockfd - ); - } - } - // a request is sent from the client - else if (!handle_http_request(i,&player1,&player2)) - { - close(i); - FD_CLR(i, &masterfds); - } - } - } - - return 0; -} - -// find current player -Player* current_player(int sockfd,Player *player1, Player *player2){ - Player* playerbot; - if(player1->player_socket==sockfd){ - return player1; - }else if(player2->player_socket == sockfd){ - return player2; - } - return playerbot; -}