-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.c
300 lines (228 loc) · 8.62 KB
/
server.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#define _POSIX_C_SOURCE 200112L
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/un.h>
#include<sys/time.h>
#include<sys/socket.h>
#include<sys/select.h>
#include"hash.h"
#include"conn.h"
#include"server_lib.h"
/******* COSTANTI *******/
#define HASH_SIZE 100
#define TIMEOUT_SELECT 10000
/******* VARIABILI GLOBALI *******/
hash_t *hash_table; //tabella hash per tenere traccia dei client connessi
char *data="./data"; //directory dove sono salvati tutti gli object
statistics_t statistiche; //statistiche del server
static volatile sig_atomic_t stop=0; //se settata a true fa terminare il server
static volatile sig_atomic_t thread_conn=0; //tiene conto del numero di thread attivi
pthread_mutex_t mutex_stat=PTHREAD_MUTEX_INITIALIZER; //mutex per le statistiche
pthread_mutex_t mutex_hash_table=PTHREAD_MUTEX_INITIALIZER; //mutex per l'hash table
/******* FUNZIONI *******/
void *thread_handler(); //gestisce i segnali
void create_thread_handler(); //crea un thread handler
void *thread_worker(void *arg); //gestisce la connessione col client
void create_thread_worker(long fd); //crea un thread worker per connessione
void cleanup(){ //funzione di cleanup quando il server termina
unlink(SOCKNAME);
delete_hash_table(hash_table);
}
void add_user(){ //aggiorna il numero di client connessi e client totali
pthread_mutex_lock(&mutex_stat);
statistiche.client_online++;
statistiche.total_client++;
pthread_mutex_unlock(&mutex_stat);
}
void remove_user(){ //aggiorna il numero di client connessi
pthread_mutex_lock(&mutex_stat);
statistiche.client_online--;
pthread_mutex_unlock(&mutex_stat);
}
/******* MAIN *******/
int main(int argc, char *argv[]){
sigset_t mask;
int sl,leave=0;
int sockid, connfd;
fd_set fdset,rfdset;
struct timeval time; //timer della select
struct sockaddr_un sa;
memset(&statistiche,0,sizeof(statistiche));
statistiche.start_time=get_time(); //per maggiori info su get_time vedere server_lib.c
mask=create_mask();
pthread_sigmask(SIG_SETMASK,&mask,NULL);
create_thread_handler();
CHECKNULL(hash_table=create_hash_table(HASH_SIZE),"Fail hash table (server, main)");
unlink(SOCKNAME);
atexit(cleanup);
if(!create_directory(data)){ //creo la directory data
printf("Errore creazione %s (server, main)\n",data);
return 0;
}
SYSCALL((sockid=socket(AF_UNIX,SOCK_STREAM,0)),"Fail socket (server, main)");
sa.sun_family=AF_UNIX;
strncpy(sa.sun_path,SOCKNAME,sizeof(SOCKNAME)+1);
SYSCALL(bind(sockid,(struct sockaddr*)&sa,sizeof(sa)),"Fail bind (server, main)");
SYSCALL(listen(sockid,MAXBACKLOG),"Fail listen (server, main)");
FD_ZERO(&fdset);
FD_SET(sockid,&fdset);
do{
rfdset=fdset; //la select resetta rfdset ad ogni ciclo
time.tv_sec=0;
time.tv_usec=TIMEOUT_SELECT; //imposto il timeout della select
SYSCALL((sl=select(sockid+1,&rfdset,NULL,NULL,&time)),"Fail select");
if(sl>0){ //se vi è un nuovo client per la connessione
SYSCALL((connfd=accept(sockid,NULL,NULL)),"Fail accept");
create_thread_worker(connfd);
}
if(stop){ //se il server deve arrestarsi attende la chiusura di tutti i client
while(thread_conn) sleep(1);
leave=1;
}
}while(!leave);
return 0;
}
/******* IMPLEMENTAZIONE FUNZIONI DICHIARATE *******/
/*
Crea un thread worker per connessione.
*/
void create_thread_worker(long fd){
pthread_t thid;
pthread_attr_t thattr;
if(pthread_attr_init(&thattr)!=0){
fprintf(stderr,"Fail pthread_attr_init (server, create_thread_worker)\n");
close(fd);
return;
}
if(pthread_attr_setdetachstate(&thattr,PTHREAD_CREATE_DETACHED)!=0){
fprintf(stderr,"Fail pthread_attr_setdetachstate (server, create_thread_worker)\n");
pthread_attr_destroy(&thattr);
close(fd);
return;
}
if(pthread_create(&thid,&thattr,&thread_worker,(void*)fd)!=0){
fprintf(stderr,"Fail pthread_create (server, create_thread_worker)\n");
pthread_attr_destroy(&thattr);
close(fd);
return;
}
}
/*
Crea un thread handler.
*/
void create_thread_handler(){
pthread_t thid;
pthread_attr_t thattr;
if(pthread_attr_init(&thattr)!=0){
fprintf(stderr,"Fail pthread_attr_ini (server, create_thread_handler)\n");
return;
}
if(pthread_attr_setdetachstate(&thattr,PTHREAD_CREATE_DETACHED)!=0){
fprintf(stderr,"Fail pthread_attr_setdetachstate (server, create_thread_handler)\n");
pthread_attr_destroy(&thattr);
return;
}
if(pthread_create(&thid,&thattr,&thread_handler,NULL)!=0){
fprintf(stderr,"Fail pthread_create (server, create_thread_handler)\n");
pthread_attr_destroy(&thattr);
return;
}
}
/*
Thread che si occupa della gesitone dei segnali.
Tutti i segnali gestiti, ad eccezione di SIGUSR1, fanno chiudere il server e i relativi thread
*/
void *thread_handler(){
int sig;
sigset_t set=create_mask();
while(!stop){
sigwait(&set,&sig);
if(sig==SIGUSR1){
print_statistics();
thread_handler(); //mi rimetto in ascolto di nuovi segnali
}
else{
stop=1;
}
}
pthread_exit(NULL);
}
/*
Thread che si occupa di servire un client.
*/
void *thread_worker(void *arg){
int write=1;
int leave=0;
char *path=NULL; //path data/user
long fd=(long) arg;
char *name_user=NULL; //variabile usata per l'hash table
thread_conn++;
do{
messaggio_t mex;
mex=raw_msg(fd); //ricevo l'operazione richiesta dal client
if(strcmp(mex.operation,"break")==0) break;
if(strcmp(mex.operation,"malformed")==0){
SYSCALL(writen(fd,mex.data,mex.len*sizeof(char)),"Fail writen (server, thread_worker - malformed)");
write=0;
break;
}
if(strcmp(mex.operation,"REGISTER")==0){
pthread_mutex_lock(&mutex_hash_table);
if(find_hash_table(hash_table,mex.name)){ //se l'utente è già connesso allora rifiuto la richiesta di register
int len=27+strlen(mex.name);
char messaggio[len];
snprintf(messaggio,len,"KO Utente %s gia' connesso \n",mex.name);
SYSCALL(writen(fd,messaggio,len*sizeof(char)),"Fail writen (server, thread_worker - register)");
leave=1; //faccio terminare il thread
}
else{
if(register_usr(mex.name,&path,data,fd)){ //se la registrazione dell'utente è andata a buon fine lo inserisco nella tabella hash
SYSCALL(insert_hash_table(hash_table,mex.name),"Fail insert_hash_table (server, thread_worker - register)");
add_user();
CHECKNULL((name_user=calloc(strlen(mex.name)+1,sizeof(char))),"Fail calloc name_user (server ,thread_worker - register");
strncpy(name_user,mex.name,strlen(mex.name)+1);
}
else leave=1; //se l'operazione di registrazione non è andata a buon fine faccio terminare il thread
}
pthread_mutex_unlock(&mutex_hash_table);
}
else if(strcmp(mex.operation,"STORE")==0){
store_data(mex,path,fd);
}
else if(strcmp(mex.operation,"RETRIEVE")==0){
retrieve_data(path,mex.name,fd);
}
else if(strcmp(mex.operation,"DELETE")==0){
delete_data(path,mex.name,fd);
}
else if (strcmp(mex.operation,"LEAVE")==0){
pthread_mutex_lock(&mutex_hash_table);
remove_hash_table(hash_table,name_user);
pthread_mutex_unlock(&mutex_hash_table);
char *messaggio="OK \n";
int len=strlen(messaggio)+1;
SYSCALL(writen(fd,messaggio,len*sizeof(char)),"Fail writen (server, thread_worker - leave)");
leave=1;
}
if(mex.data) free(mex.data);
if(mex.name) free(mex.name);
if(mex.operation) free(mex.operation);
}while(!leave && !stop);
if(!leave){ //Viene eseguito se il thread viene chiuso e non si è fatta l'operazione leave
pthread_mutex_lock(&mutex_hash_table);
remove_hash_table(hash_table,name_user);
pthread_mutex_unlock(&mutex_hash_table);
if(write) SYSCALL(writen(fd,"KO thread chiuso \n",strlen("KO thread chiuso \n")*sizeof(char)),"Fail writen (server, thread_worker)"); //se il client ha mandato un messaggio sulla socket ma non si è fatto in tempo a leggere
}
if(path!=NULL) free(path);
if(name_user!=NULL){
free(name_user);
remove_user(); //se la condizione nell'if è vera vuol dire che ho incrementato in precedenza il contatore che tiene conto degli utenti online
}
close(fd);
thread_conn--;
pthread_exit(NULL);
}