-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmsgcli.c
382 lines (288 loc) · 12.2 KB
/
msgcli.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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
/**
\file msgcli.c
\author Marco Ponza
\brief client
Si dichiara che ogni singolo bit presente in questo file è solo ed esclusivamente "farina del sacco" del rispettivo autore :D
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/un.h>
#include <pthread.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include "comsock.h"
#include "funcli.h"
/** ========== Macro ========== */
#define SOCKNAME "./tmp/msgsock"
#define NBUFFER 514 /* 256 nome destinatario + 1 spazio + 256 messaggio + 1 '\n' */
#define SERVER_DISCONNECT "Il server si e' disconnesso\n"
#define ERROR_SEND_MSG "Errore nell invio del messaggio"
#define ERROR_RECEIVE_MSG "Client Errore nella ricezione del messaggio"
#define ERR_TYPE -3 /* valore che indica che la stringa inserita su stdin è errata */
/** ========== Variabili globali ========== */
pthread_t handler; /* variabile globale per far terminare l handler in caso di %EXIT */
pthread_t sender; /* variabile globale per far terminare il sender in caso di SIGINT o SIGTERM */
pthread_t receiver; /* variabile globale per far terminare il receiver in caso di SIGINT o SIGTERM */
void * Handler (void * not_used) {
int signum;
sigset_t set;
/******************************************************************/
/** ========== Setting e attesa dei segnali da gestire ========== */
/******************************************************************/
if (sigemptyset ( &set ) == -1 ) {
perror ("Errore durante il mascheramento dei segnali");
exit (EXIT_FAILURE);
}
sigaddset (&set, SIGINT);
sigaddset (&set, SIGTERM);
sigaddset (&set, SIGUSR1); /* quando deve killare il sender */
sigaddset (&set, SIGUSR2); /* quando non deve killare nessuno */
if ( pthread_sigmask (SIG_SETMASK, &set, NULL) == -1 ) {
fprintf (stderr, "Errore durante il mascheramento dei segnali");
exit (EXIT_FAILURE);
}
/* attesa dell'arrivo di uno dei segnali settati */
if ( sigwait ( &set, &signum ) != 0) {
fprintf (stderr, "Errore durante l'esecuzione di sigwait");
exit (EXIT_FAILURE);
}
/********************************************************/
/** ========== Gestione dei segnali ricevuti ========== */
/********************************************************/
if (signum == SIGINT || signum == SIGTERM) {
/* cancellazione dei thread Sender e receiver */
pthread_cancel (sender);
pthread_cancel (receiver);
return NULL;
}
if (signum == SIGUSR2) {
/* non deve killare nessun thread */
return NULL;
}
/* signum == SIGUSR1 */
/* è terminata la connessione con il server */
pthread_cancel (sender);
return NULL;
}
void * Sender (void * fd_socket)
{
int i, n;
int skt = * ((int *) fd_socket);
int old;
char buf [NBUFFER];
message_t msg;
char * eof; /* se è stato letto un EOF il suo valore è NULL */
while (1) {
n = ERR_TYPE;
eof = fgets (buf, NBUFFER + 1, stdin);
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE, &old );
/***************************************************************************/
/** ========== Lettura EOF o di un messaggio per disconnettersi ========== */
/***************************************************************************/
if (eof == NULL || strncmp (buf, "%EXIT", 5) == 0) {
msg.type = MSG_EXIT;
msg.length = 0;
msg.buffer = NULL;
pthread_kill (handler, SIGUSR2); /* notifico l'handler che non dovrà cancellare il sender perché terminerà da solo */
Send_skt (skt, &msg);
return NULL;
}
/***********************************************/
/** ========== Messaggio di listing ========== */
/***********************************************/
if (strncmp (buf, "%LIST", 5) == 0) {
msg.type = MSG_LIST;
msg.length = 0;
n = Send_skt (skt, &msg);
}
/************************************************************/
/** ========== Messaggio da inviare ad un client ========== */
/************************************************************/
if (strncmp (buf, "%ONE ", 5) == 0) {
buf [strlen (buf) - 1] = '\0'; /* (strlen (buf) - 1) posizione in cui si trova '\n' */
Left_shift (buf, 5); /* tolgo dal buffer la stringa "%ONE " */
if (To_one_good_str (buf) == 0) { /* stringa digitata non è corretta */
fprintf (stderr, "\n\n***** WARNING *****\nAttenzione, errato inserimento della stringa.\nPer inviare una richiesta al server digitare:\n\t%%EXIT - per disconnettersi\n\t%%LIST - per ricevere la lista degli utenti connessi al server\nPer inviare un messaggio ad un particolare utente digitare:\n\t%%ONE \"nomeutente\" \"messaggio\"\nPer inviare un messaggio a tutti gli utenti collegati al server digitare semplicemente il messaggio.\nSi ricorda che il messaggio inviato deve contenere solamente carattere stampabili escluso il carattere %%\n\n");
} else { /* la stringa seguita da "%ONE " rispetta la nostra sintassi */
msg.type = MSG_TO_ONE;
msg.length = strlen (buf) + 1;
msg.buffer = malloc (sizeof (char) * (msg.length));
sprintf (msg.buffer, "%s", buf);
for (i = 0; (msg.buffer) [i] != ' '; i++);
msg.buffer [i] = '\0'; /* inserisco il terminatore dopo il nome del destinatario */
n = Send_skt (skt, &msg);
free (msg.buffer);
}
} else { /* a questo punto può essere solamente un messaggio di broadcast */
/*************************************************/
/** ========== Messaggio di broadcast ========== */
/*************************************************/
if (buf [0] != '%') {
buf [strlen (buf) - 1] = '\0';
if (Is_good_str (buf) == 0) { /* stringa digitata non è corretta */
fprintf (stderr, "\n\n***** WARNING *****\nAttenzione, errato inserimento della stringa.\nPer inviare una richiesta al server digitare:\n\t%%EXIT - per disconnettersi\n\t%%LIST - per ricevere la lista degli utenti connessi al server\nPer inviare un messaggio ad un particolare utente digitare:\n\t%%ONE \"nomeutente\" \"messaggio\"\nPer inviare un messaggio a tutti gli utenti collegati al server digitare semplicemente il messaggio.\nSi ricorda che il messaggio inviato deve contenere solamente carattere stampabili escluso il carattere %%\n\n");
} else {
msg.type = MSG_BCAST;
msg.buffer = buf;
msg.length = strlen (buf) + 1;
n = Send_skt (skt, &msg);
}
}
}
if (n == SEOF) { /* il server ha chiuso la connessione */
pthread_kill (handler, SIGUSR2); /* notifico l'handler che non dovrà cancellare il sender perché terminerà da solo */
return NULL;
}
if (n == ERR_TYPE) { /* non è nessuno dei tipi dei messaggi precedenti */
fprintf (stderr, "\n\n***** WARNING *****\nAttenzione, errato inserimento della stringa.\nPer inviare una richiesta al server digitare:\n\t%%EXIT - per disconnettersi\n\t%%LIST - per ricevere la lista degli utenti connessi al server\nPer inviare un messaggio ad un particolare utente digitare:\n\t%%ONE \"nomeutente\" \"messaggio\"\nPer inviare un messaggio a tutti gli utenti collegati al server digitare semplicemente il messaggio.\nSi ricorda che il messaggio inviato deve contenere solamente carattere stampabili escluso il carattere %%\n\n");
}
pthread_setcancelstate ( PTHREAD_CANCEL_ENABLE, &old );
}
return NULL;
}
void * Receiver (void * fd_socket)
{
int n = 0;
int skt = * ((int *) fd_socket);
int old; /* necessaria per abilitare/disabilitare la cancel */
message_t msg;
while (1) {
n = receiveMessage (skt, &msg);
pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE, &old ); /* in questo modo può visualizzare i messaggi ricevuti
* se nel mentre riceve una pthread_cancel
*/
/***********************************************/
/** ========== Connessione conclusa ========== */
/***********************************************/
if (n == SEOF) {
/* il server ha chiuso la connessione */
pthread_kill ( handler, SIGUSR1 ); /* avviso l'handler che, se non ha ricevuto anche SIGUSR2, dovrà killare il sender */
return NULL;
}
/*********************************************/
/** ========== Messaggio d'errore ========== */
/*********************************************/
if (msg.type == MSG_ERROR) {
printf ("[ERROR] %s\n", msg.buffer);
fflush (stdout);
}
/***********************************************/
/** ========== Messaggio di listing ========== */
/***********************************************/
if (msg.type == MSG_LIST) {
printf ("[LIST] %s\n", msg.buffer);
fflush (stdout);
}
/**********************************************************/
/** ========== Messaggio da parte di un client ========== */
/**********************************************************/
if (msg.type == MSG_TO_ONE) {
printf ("%s\n", msg.buffer);
fflush (stdout);
}
/************************************************************************/
/** ========== Messaggio dri broadcast da parte di un client ========== */
/************************************************************************/
if (msg.type == MSG_BCAST) {
printf ("[BCAST]%s\n", msg.buffer);
fflush (stdout);
}
free (msg.buffer);
pthread_setcancelstate ( PTHREAD_CANCEL_ENABLE, &old );
}
return NULL;
}
int main (int argc, char * argv [])
{
int skt, n;
message_t msg;
sigset_t set;
struct sigaction sa;
if (argc != 2) {
perror ("L'applicazione msgcli deve essere eseguita come: \"$ msgcli username\"\n");
exit (EXIT_FAILURE);
}
if ( (skt = openConnection(SOCKNAME)) < 0 ) {
perror ("Impossibile comunicare con il server");
exit (EXIT_FAILURE);
}
/*************************************************************/
/** ========== Setting della maschera dei segnali ========== */
/*************************************************************/
if (sigfillset ( &set ) == -1 ) {
perror ("Errore durante il mascheramento dei segnali");
exit (EXIT_FAILURE);
}
if ( pthread_sigmask (SIG_SETMASK, &set, NULL) == -1 ) {
fprintf (stderr, "Errore durante il mascheramento dei segnali");
exit (EXIT_FAILURE);
}
/* ignoro sigpipe */
bzero (&sa, sizeof (sa));
sa.sa_handler = SIG_IGN;
sigaction (SIGPIPE, &sa, NULL);
/***************************************************/
/** ========== Richiesta di connessione ========== */
/***************************************************/
msg.type = MSG_CONNECT;
msg.buffer = argv [1];
msg.length = strlen (argv [1]) + 1;
n = sendMessage (skt, &msg);
if (n == SEOF) {
fprintf (stderr, SERVER_DISCONNECT);
Close_skt (skt);
exit (EXIT_FAILURE);
}
if (n == -1) {
perror (ERROR_SEND_MSG);
Close_skt (skt);
exit (EXIT_FAILURE);
}
n = receiveMessage (skt, &msg);
/* Connessione fallita o rifiutata */
if (n == SEOF) {
fprintf (stderr, SERVER_DISCONNECT);
Close_skt (skt);
exit (EXIT_FAILURE);
}
if (n == -1) {
perror (ERROR_RECEIVE_MSG);
Close_skt (skt);
exit (EXIT_FAILURE);
}
if (msg.type == MSG_ERROR) {
fprintf (stderr, "%s", msg.buffer);
free (msg.buffer);
Close_skt (skt);
exit (EXIT_FAILURE);
}
/* Abilitato alla connessione sul server */
/**********************************************************/
/** ========== Creazione dei thread del client ========== */
/**********************************************************/
if ( pthread_create (&sender, NULL, Sender, &skt) != 0 ) {
fprintf (stderr, "Errore durante la creazione del thread Sender");
Close_skt (skt);
}
if ( pthread_create (&receiver, NULL, Receiver, &skt) != 0 ) {
fprintf (stderr, "Errore durante la creazione del thread Receiver");
Close_skt (skt);
}
if ( pthread_create (&handler, NULL, Handler, NULL) != 0 ) {
fprintf (stderr, "Errore durante la creazione del thread Receiver");
Close_skt (skt);
}
/*******************************************************************************/
/** ========== Attesa della corretta terminazione di tutti i thread ========== */
/*******************************************************************************/
pthread_join ( handler, NULL ); /* caso in cui si riceve una SIGINT o SIGTERM */
pthread_join ( sender, NULL ); /* caso in cui mentre si sta scrivendo sulla socket il server la chiude */
pthread_join ( receiver, NULL );
Close_skt (skt);
return 0;
}