Skip to content
Snippets Groups Projects
Commit a2f6ca27 authored by Anqi Chen's avatar Anqi Chen
Browse files

init

parents
No related branches found
No related tags found
No related merge requests found
Pipeline #1777 canceled
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Image Tagger Game</h2>
<img src="https://swift.rc.nectar.org.au/v1/AUTH_eab314456b624071ac5aecd721b977f0/comp30023-project/image-3.jpg" alt="HTML5 Icon" style="width:700px;height:400px;">
<p>Welcome to the image tagging game. Please enter your name below.</p>
<form method="POST">
Name: <input type="text" name="user" />
<input type="submit" class="button" />
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Image Tagger Game</h2>
<img src="https://swift.rc.nectar.org.au/v1/AUTH_eab314456b624071ac5aecd721b977f0/comp30023-project/image-3.jpg" alt="HTML5 Icon" style="width:700px;height:400px;">
<form method="GET">
<input type="submit" class="button" name="start" value="Start"/>
</form>
<form method="POST">
<input type="submit" class="button" name="quit" value="Quit"/>
</form>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>You are ready now!</h2>
<img src="https://swift.rc.nectar.org.au/v1/AUTH_eab314456b624071ac5aecd721b977f0/comp30023-project/image-2.jpg" alt="HTML5 Icon" style="width:700px;height:400px;">
<p>Rule: Try to guess the above image by typing a keyword which describes it:</p>
<form method="POST">
Keyword: <input type="text" name="keyword" />
<input type="submit" class="button" name="guess" value="Guess" />
</form>
<form method="POST">
<input type="submit" class="button" name="quit" value="Quit"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Keyword Accepted! Keep trying more.</h2>
<img src="https://swift.rc.nectar.org.au/v1/AUTH_eab314456b624071ac5aecd721b977f0/comp30023-project/image-2.jpg" alt="HTML5 Icon" style="width:700px;height:400px;">
<p>Rule: Try to guess the above image by typing a keyword which describes it:</p>
<form method="POST">
Keyword: <input type="text" name="keyword" />
<input type="submit" class="button" name="guess" value="Guess" />
</form>
<form method="POST">
<input type="submit" class="button" name="quit" value="Quit"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Keyword Discarded. The other player is not ready yet.</h2>
<img src="https://swift.rc.nectar.org.au/v1/AUTH_eab314456b624071ac5aecd721b977f0/comp30023-project/image-2.jpg" alt="HTML5 Icon" style="width:700px;height:400px;">
<p>Rule: Try to guess the above image by typing a keyword which describes it:</p>
<form method="POST">
Keyword: <input type="text" name="keyword" />
<input type="submit" class="button" name="guess" value="Guess" />
</form>
<form method="POST">
<input type="submit" class="button" name="quit" value="Quit"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Thanks for playing. The game is completed.</h2>
<p>Would you like to play it again?</p>
<form method="GET">
<input type="submit" class="button" name="start" value="Start"/>
</form>
<form method="POST">
<input type="submit" class="button" name="quit" value="Quit"/>
</form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Game Over!</h2>
</body>
</html>
CC = gcc
CFLAGS = -std=c99 -O3 -Wall -Wpedantic
# CFLAGS = -std=c99 -O3 -Wall -Wpedantic -g
SRC = image_tagger.c
TARGET = image_tagger
all: $(SRC)
$(CC) $(SRC) -o $(TARGET) $(CFLAGS)
clean:
rm -f $(TARGET) *.o
Computer System Project1
Background. Nowadays, search engines have been an essential part of our lives, e.g., Google and
Bing, which have significantly improved our productiveness. There are various types of searching
services, among which keyword search is the dominant one, namely searching by keywords. While
keyword search works well for documents, pages and websites, it faces a big challenge when searching
for images. This is because linking images with keywords is typically difficult for computers as it
requires understanding on the semantic of images. For this purpose, tagging images with keywords
is one of the most effective ways to help computers understand images’ semantic, and this is
also the first step to help machines learn (by telling them which is correct and which is wrong).
Unfortunately, tagging a large number of images is boring and requires a substantial amount of
human power. In this project, you will make the boring tagging process more funny by implementing
a network game server that tags images as people play. (The idea was invented by the inventor
of re-Captcha and Duo-lingo; Google bought the company and shut down the game.)
The Rules of the Game. The game consists of two players and one server, where each of the
players can only communicate with the server (but the other player). At the beginning, when the
two players log on to the server, the server sends both of them a same image. The game then
starts; the players submit words or phrases to the server, one at a time, without being told what
the other player has inputed. The goal for both the players is to enter a word or a phrase that has
been submitted by the other as soon as possible. Obviously, in order to maximise the chance of
getting a match, the two players would better to enter words that describe the image well, since it
is the only information shared by both of the two. (The image is, therefore, tagged.) Once the goal
is achieved, the game ends; the server sends a web page indicating that the game is completed and
prompting the players to play again. If both the players agree to play again, the process repeats
with a new image.
Project Basic Functionalities. In this project, you are asked to write a program to implement
(by socket programming in C) the aforementioned game server supporting two players from
two different browsers over HTTP. The program should allow users to configure both the server IP
address and the port number. Specifically, you need to implement the following basic functionalities
of the server, where the html source files of all the pages will be provided.
h.c 0 → 100644
/*
** code by Angela Chan
*/
#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/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_BUFF 2049
// cookie for user, formtted with userID
static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
Content-Length: %ld\r\n\
Content-Type: text/html\r\n\
Set-Cookie: cookie_id= %d\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;
// 3 kind of methods
typedef enum
{
GET,
POST,
UNKNOWN
} METHOD;
// struct User
struct User {
int userID;
char* username;
char** keywords;
int isOnline; // Online=1, OffLine=0
int stage;
int n_word;
};
// malloc a newUser
struct User* newUser() {
struct User* newUser = malloc(sizeof (struct User));
newUser->userID = 0;
//newUser->username = malloc(MAX_BUFF*sizeof(char));
//newUser->keywords = NULL;
newUser->keywords =malloc(MAX_BUFF*sizeof(char*));
for (int i = 0; i < MAX_BUFF; i++)
{
newUser->keywords[i] = NULL;
}
newUser->isOnline = 0;
newUser->stage = 0;
newUser->n_word = 0;
return newUser;
}
void clear_user_keyword(struct User* user){
for (int i = 0; i < MAX_BUFF; i++)
{
user->keywords[i] = NULL;
}
}
// abstract the cookie from http response in buff
int find_cookie(char* buff)
{
char* cookie_id=strstr(buff, "cookie_id=");
char* temp;
if(cookie_id == NULL)
{
return 0;
}
else
{
temp = malloc(sizeof(char)*strlen(cookie_id)+1);
strncpy(temp, cookie_id, strlen(cookie_id));
temp+=10;
}
printf("find_cookie in buff %s\n", cookie_id);
//int cookie_length = strlen(username);
temp[strlen(temp)-3]='\0';
printf("find_cookie in buff %s\n", cookie_id);
return atoi(temp);
}
//find the current user
struct User* find_curr(struct User* user_arr[], char* buff)
{
int cookie_id = find_cookie(buff);
printf("the kmimimji%d %d\n", cookie_id,user_arr[0]->userID);
if(cookie_id==0){
struct User* curr_user = newUser();
if ((cookie_id!=1||cookie_id!=2)&& user_arr[0]->userID!=1)
{
curr_user->userID = 1;
user_arr[0]= curr_user;
}
else if((cookie_id!=1||cookie_id!=2)&& user_arr[1]->userID!=2){
curr_user->userID = 2;
user_arr[1]= curr_user;
}
cookie_id=curr_user->userID;
printf(" func curr: cookie_id is %d\n%d\n\n\n", cookie_id,curr_user->userID);
}
for (int i=0; i<2; i++) {
if (user_arr[i]->userID == cookie_id)
{
printf("existing curr user %d , with cookie_id %d", i,cookie_id);
return user_arr[i];
}
}
}
// get the other user
struct User* the_other_user(struct User* curr_user, struct User* user_arr[]){
for(int i=0; i<2; i++){
if (user_arr[i]!=curr_user){
return user_arr[i];
}
}
}
// simply load the original html
void simple_load_html(char* html, int sockfd, char* buff, int n, struct User* curr_user){
struct stat st;
stat(html, &st);
n = sprintf(buff, HTTP_200_FORMAT, st.st_size+6,curr_user->userID);
if (write(sockfd, buff, n) < 0)
{
perror("write");
return ;
}
int filefd = open(html, O_RDONLY);
do
{
n = sendfile(sockfd, filefd, NULL, 2048);
}
while (n > 0);
if (n < 0)
{
perror("sendfile");
close(filefd);
return ;
}
close(filefd);
}
// plug in the new words in new buff to the old buff
void plug_in_html(char* newword, char* old_buffer, char* new_buffer){
// split buff according to </body>
char start[MAX_BUFF];
char* end = strstr(old_buffer, "</body>");
// copy and close up
strncpy(start, old_buffer, strlen(old_buffer)-strlen(end));
start[strlen(start)] = '\0';
// merge the buffs
strncpy(new_buffer, start, strlen(start));
strcat(new_buffer, newword);
strcat(new_buffer, end);
new_buffer[strlen(new_buffer)] = '\0';
}
// if the html is rearranged, load it with the new content
void edit_load_html(char* html, int sockfd, char* buff, char* newword,struct User* curr_user){
// get the size of the file
struct stat st;
stat(html, &st);
// increase file size to accommodate the new guess
int added_length= strlen(newword);
long size = st.st_size + added_length;
int n = sprintf(buff, HTTP_200_FORMAT, size+6, curr_user->userID);
// send the header first
if (write(sockfd, buff, n) < 0)
{
perror("write");
return;
}
// read the content of the HTML file
int filefd = open(html, O_RDONLY);
n = read(filefd, buff, st.st_size);
if (n < 0)
{
perror("read");
close(filefd);
return;
}
close(filefd);
// makes new buffer, copies the username into it
char* new_buffer = (char *) malloc(sizeof(MAX_BUFF));
plug_in_html(newword, buff, new_buffer);
if (write(sockfd, new_buffer, size) < 0)
{
perror("write");
return;
}
free(new_buffer);
}
// read the new guess word into the user keyword array
void record_keyword(struct User* curr_user, char *new_guess){
for (int i=0; i<=curr_user->n_word; i++){
if(curr_user->keywords[curr_user->n_word]==NULL){
curr_user->keywords[curr_user->n_word] = malloc((1 + strlen(new_guess)) * sizeof(char*));
strncpy(curr_user->keywords[curr_user->n_word],new_guess, strlen(new_guess));
curr_user->n_word++;
break;
}
}
}
// find if word is in array, knowing the length of the array
bool match(char* w1, char* w2[], int n_word){
for (int i=0; i<n_word; i++){
if (w2[i]==w1){
return true;
}
}
return false;
}
static bool handle_http_request(int sockfd, struct User* user_arr[])
{
// try to read the request
char buff[2049];
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;
}
printf("buff1: \n");
printf("%s\n",buff);
// get current user
struct User* curr_user=find_curr(user_arr,buff);
// sanitise the URI
while (*curr == '.' || *curr == '/')
++curr;
// assume the only valid request URI is "/" but it can be modified to accept more files
if (*curr == ' ' || *curr == '?')
//Stage1: when user name is not registed
if (curr_user->stage==0){
// the only GET method before registing name is the intro page
if (method==GET)
{
printf("curr_user->username%s == %d\n", curr_user->username,curr_user->username==NULL);
simple_load_html("1_intro.html",sockfd,buff,n,curr_user);
}
// the only POST method before registing name is the start page
else if (method == POST)
{
printf("curr_user->username%s == %d\n", curr_user->username,curr_user->username==NULL);
// locate the username, it is safe to do so in this sample code, but usually the result is expected to be
// copied to another buffer using strncpy or strncpy to ensure that it will not be overwritten.
char * user = strstr(buff, "user=") + 5;
char username[MAX_BUFF];
strncpy(username, "<br>",strlen("<br>"));
strcat(username, user);
username[strlen(username)] = '\0';
edit_load_html("2_start.html", sockfd,buff,username,curr_user);
printf("buffer %s\n",buff );
curr_user->stage=1;
}
}
//Stage 1: start the game
else if(curr_user->stage==1)
{
// quit: user not online
if(strstr(buff, "quit=Quit") != NULL)
{ curr_user->isOnline=0;
simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
}
// start is the only GET mathod for ther stage 1
else if (method == GET)
{ curr_user->isOnline=1;
simple_load_html("3_first_turn.html",sockfd,buff,n,curr_user);
curr_user->stage=2;
}
}
// Stage 2: in the process of guessing
else if (curr_user->stage ==2){
// ready to guess
char * new_guess = strstr(buff, "keyword=") + 8;
// if quit, offline and quit
// clear all user info
if(strstr(buff, "quit=Quit") != NULL)
{ curr_user->isOnline=0;
simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
clear_user_keyword(curr_user);
}
// other user offline, discard
if (the_other_user(curr_user, user_arr)->isOnline==0){
simple_load_html("5_discarded.html",sockfd,buff,n,curr_user);
clear_user_keyword(curr_user);
}
// GET method can only do start in stage2
// clear the user keyword array
if (method = GET) {
clear_user_keyword(curr_user);
simple_load_html("3_first_turn.html",sockfd,buff,n,curr_user);
}
// two players guess match
if (new_guess != NULL)
{
//if match game end
if (match(new_guess, the_other_user(curr_user, user_arr)->keywords, the_other_user(curr_user, user_arr)->n_word))
{
simple_load_html("6_endgame.html",sockfd,buff,n,curr_user);
clear_user_keyword(curr_user);
}
// if a new guess is take, not match, add it to array and display it
else
{
curr_user->stage =2;
printf("curr_user->n_word%d\n",curr_user->n_word );
// remember new guess
record_keyword(curr_user, new_guess);
// display new guess
char guess[MAX_BUFF];
strncpy(guess,"<br>",strlen("<br>"));
if (curr_user->n_word==1){
strcat(guess, new_guess);
guess[strlen(new_guess)]='\0';
}else
{
for (int i = 0; i < curr_user->n_word; i++)
{
strcat(guess, curr_user->keywords[i]);
strcat(guess,"<br>");
}
}
edit_load_html("4_accepted.html", sockfd,buff,guess,curr_user);
}
}
}
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]));
// 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;
//global variable for 2 users
struct User* user_arr[2];
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, user_arr))
{
close(i);
FD_CLR(i, &masterfds);
}
}
}
return 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>COMP30023 HTTP Server</title>
</head>
<body>
<h1>COMP30023 HTTP Server</h1>
<img
src="https://brandhub.unimelb.edu.au/guidelines/logos/04_Logo_Vertical-Housed.jpg"
alt="HTML5 Icon"
style="width:400px;height:400px;"
/>
<h2>Thank you!</h2>
</body>
</html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment