PA3

ewillwin·2022년 6월 2일
0

System Programming Lab

목록 보기
14/15
post-thumbnail

You have to implement a ticketing server which is capable of handling numerous clients simultaneously. The server should manage the ticketing procedure and seat position of each client. A client sends queries to the server to get a ticket or a desired seat. A single query represents an action that client can take.

Server
A server takes queries from clients in an infite loop with the following condition.

  • 전체 좌석 = 256개
  • 각 좌석은 최소한 한개의 synchronization mechanism에 의해 관리되어야함
  • server는 최대 1024개의 clients를 동시에 다룰 수 있음
  • 만약 연결이 성공적으로 성립됐다면, dedicated thread가 client를 위해 생성되어야함
  • server는 각 "user"의 log-in 상태를 관리해야함/ "user" != "client" -> "client"는 server와 communicate하는 program or process이고, 특정 "user"의 query는 이 client prgram을 이용해 server로 전달될 수 있다
  • user id 존재
  • If user 1 is currently logged in through client A, every other attempt to log-in as user 1 from the other client must be blocked by the server. And also, the client A cannot send queries of another user before the user 1 successfully log out.
  • server는 user가 좌석을 예약하기 전에 log in 돼있는지를 먼처 check해야함
  • server는 query를 받고 나서 response code를 client한테 보내야함 -> 2.3
  • about termiation condition of the server -> 2.4

Client query
아래의 형식을 따르는 하나의 query가 client에서 server로 보내짐

struct query {
	int user;
    int action;
    int data;
}

user -> user id (0 ~ 1023)
action -> action id (1 ~ 5, 0은 terminate)
data -> 각 action에 필요한 data
만약 the user of action field가 range를 벗어난다면, server는 -1 반환

Action in query
5개의 action이 있음

-> Log in

  • If the user trying to log in for the first time, the action will be treated as “registration”. Then the server will initialize the passcode for the user with the data field in query. The passcode is a 4-byte integer.
  • If already registered user tries to log in with the wrong passcode, the action will fail.
  • If the user trying to log in is currently logged in at the other client, the action will fail.
  • The action will succeed otherwise.
  • The server returns 1 on success, -1 on fail.

-> Reserve

  • Server makes a reservation for a user
  • The seat number in data field ranges from 0 to 255. The action will fail if the seat number is out of range.
  • If the user tries to take this action before logging in, the action will fail.
  • If the seat requested by the user is already reserved by the other user, the action will fail.
  • The action will succeed otherwise.
  • The server returns reserved seat number on success, -1 on fail.

-> Check reservation

  • If the user tries to take this action before logging in, the action will fail.
  • If the user did not reserve any seat, the action will fail.
  • The action will succeed otherwise.
  • The server returns the reserved seat number on success, -1 on fail.

-> Cancel reservation

  • If the user tries to take this action before logging in, the action will fail.
  • If the user did not reserve any seat, the action will fail.
  • The action will succeed otherwise.
  • The server returns the cancelled seat number on success, -1 on fail.

-> Log out

  • If the user tries to take this action before logging in, the action will fail.
  • The action will succeed otherwise.
  • The server returns 1 on success, -1 on fail.

Response code summary
action과 그에 상응하는 response code들에 대한 table
1, Log in, 1, -1

Termination condition

  • This termination query is sent when all the reservations are done. (No log-in required)
  • When the server receives the query in which all the fields (action, user, data) are zero,
    server must return the whole integer seat array.
  • The size of the array must be 256 and the value of the array element must be the owner of the
    corresponding seat. For example, if user 3 successfully reserved seat 14, the value of 14th
    element in the array should be 3.
  • If the seat is not reserved any user, the value of the element must be -1.

Client/server examples


06.04
-> multithread server 틀 짜기 완료

남은 거
-> query 조건 다시 확인
-> pthread_mutex 임계 영역 확인
1. If user 1 is currently logged in through client A, every other attempt to log-in as user 1 from the other client must be blocked by the server. And also, the client A cannot send queries of another user before the user 1 successfully log out.
2. When the server receives the query in which all the fields (action, user, data) are zero, server must return the whole integer seat array.
3. The size of the array must be 256 and the value of the array element must be the owner of the corresponding seat. For example, if user 3 successfully reserved seat 14, the value of 14th element in the array should be 3.
4. reserve action에서 seat이 차있으면 ignored. return -1.

-> pthread_mutex 임계 영역 확인
1. user 1이 client A에서 log in 상태이면, 다른 client에서 user 1 로 log in 할 수 없다.
2. user 1이 성공적으로 log out 하기 전까지 client A는 다른 user에서 query를 보낼 수 없다.

-> seat만 synchronization 해주고, clog[1024]로 현재 로그인한 state 저장

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>

#define SIZE 12

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

struct login{
	int log; //login state -1 or 1
	int passcode; //passcode
	int st; //seat number
};
typedef struct _query {
        int user; //0 ~ 1023
        int action; //1 ~5, 0 to terminate
        int data; //
} query;

struct login l[1024]; //struct array that represent each user state!
int seat[256]; //seat array! seat number range is 0 ~ 255
int ret[2] = {-1, 1}; //simple return value
int clog[1014]; //current login state (for mutex)

void *thread_func(void *arg)
{
	int n;
	query q;

	int connfd = *((int*)arg);
	pthread_detach(pthread_self());
	free(arg);
	
	while(1){
		n = read(connfd, &q, sizeof(q)); //read query
	
		if (q.user == 0 && q.action == 0 && q.data == 0){ //terminate query
			write(connfd, seat, sizeof(seat));
			break;
		}
		if (q.action == 1){ //log in 1
			if (l[q.user].passcode == -1 && clog[q.user] == 0){ //not been registered
				l[q.user].log = 1; //login state set
				l[q.user].passcode = q.data; //insert passcode
				//pthread_mutex_lock(&mutex);
				clog[q.user] = 1;
				//pthread_mutex_unlock(&mutex);
				printf("log in complete\n");
				write(connfd, &ret[1], sizeof(ret[1]));
			}
			else if (l[q.user].passcode == q.data && clog[q.user] == 0){ //been registered
				l[q.user].log = 1; //login state set
				//pthread_mutex_lock(&mutex);
				clog[q.user] = 1;
                                //pthread_mutex_unlock(&mutex);
				printf("log in complete\n");
				write(connfd, &ret[1], sizeof(ret[1]));
			}
			else{
				printf("log in fail\n");
				write(connfd, &ret[0], sizeof(ret[0]));
			}
			continue;
		}
		else if (q.action == 2){ //reserve 2
			if (q.data >= 0 && q.data <=255 && l[q.user].log == 1 && seat[q.data] == -1){
				pthread_mutex_lock(&mutex);
				seat[q.data] = q.user; //seat <- user id
				l[q.user].st = q.data; //also update user state
				pthread_mutex_unlock(&mutex);
				printf("reserve complete\n");
				write(connfd, &q.data, sizeof(q.data));
			}
			else{
				printf("reserve fail\n");
				write(connfd, &ret[0], sizeof(ret[0]));
			}	
			continue;
		}
		else if (q.action == 3){ //check reservation 3
			if (l[q.user].log == 1 && l[q.user].st != -1){
				printf("check reservation complete\n");
				write(connfd, &l[q.user].st, sizeof(l[q.user].st));
			}
			else{
				printf("reserve fail\n");
				write(connfd, &ret[0], sizeof(ret[0]));
			}
			continue;
		}
		else if (q.action == 4){ //cancel reservation 4
			if (l[q.user].log == 1 && seat[q.data] != -1){
				pthread_mutex_lock(&mutex);
				seat[q.data] = -1;
				l[q.user].st = -1;
				pthread_mutex_unlock(&mutex);
				printf("cancel reservation complete\n");
				write(connfd, &q.data, sizeof(q.data));
			}
			else{
				printf("cancel reservation fail\n");
				write(connfd, &ret[0], sizeof(ret[0]));
			}
			continue;
		}
		else if (q.action == 5){ //log out 5
			if (l[q.user].log == 1 && clog[q.user] == 1){
				l[q.user].log = -1;
				//pthread_mutex_lock(&mutex);
				clog[q.user] = 0;
                                //pthread_mutex_unlock(&mutex);
				printf("log out complete\n");
				write(connfd, &ret[1], sizeof(ret[1]));
			}
			else{
				printf("log out fail\n");
				write(connfd, &ret[0], sizeof(ret[0]));
			}
			continue;
		}
		else{
			printf("query fail\n");
			write(connfd, &ret[0], sizeof(ret[0]));
			continue;
		}
	}
	
	close(connfd);
	return NULL;
}

int main(int argc, char *argv[])
{	
	int n, listenfd, caddrlen, nbytes;
	struct sockaddr_in saddr, caddr;
	struct hostent *h;
	int port = atoi(argv[1]);

	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0){
		printf("socket() failed.\n");
		exit(1);
	} //listen file descriptor (server)

	memset((char *)&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	saddr.sin_port = htons(port);
	
	if (bind(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){
		printf("bind() failed.\n");
		exit(2);
	}
	if (listen(listenfd, 5) < 0){
		printf("listen() failed.\n");
		exit(3);
	}

	for (int i=0; i<256; i++)
		seat[i] = -1; //seat initialize
	for (int i=0; i<1024; i++){
		l[i].log = -1; //user login status initialize
		l[i].passcode = -1; //user passcode initialize
		l[i].st = -1; //user seat number initialize
		clog[i] = 0; //sss initialize 0->login off / 1->login on
	}

	int *connfdp;
	pthread_t tid;
	while(1){
		connfdp = (int *)malloc(sizeof(int));
		caddrlen = sizeof(caddr);
		if ((*connfdp = accept(listenfd, (struct sockaddr *)&caddr, (socklen_t *)&caddrlen)) < 0){
			printf("accept() failed.\n");
			free(connfdp);
			continue;
		}
		printf("wait query ...\n");

		pthread_create(&tid, NULL, thread_func, connfdp);

	}
	
	pthread_mutex_destroy(&mutex);
	free(connfdp);
	return 0;
}
profile
💼 Software Engineer @ LG Electronics | 🎓 SungKyunKwan Univ. CSE

0개의 댓글