La Programmazione Client/Server in C
21 Dicembre 2007 Reti
   





Accettazione di una Richiesta di Connessione del Client da parte del Server

Nel momento in cui il server si è posto in ascolto nel canale di comunicazione rappresentato da una socket e giunge una richiesta di connessione il server l’accetta ed instaura una connessione con il client richiedente.
Nel caso in cui ci siano più richieste di connessione a formare una coda il server accetterà ed esaudirà le richieste nell’ordine in cui queste sono arrivate.

Figura 4.

Per far accettare la richiesta di connessione al server si usa la funzione accept.  
  1. /* La socket accetta la richiesta di connessione del Client */
  2. sin_size = sizeof(struct sockaddr_in);
  3. remoteSocket = accept(listenSocket, (struct sockaddr *)&Client_addr,
  4. &sin_size);
  5. printf("Accettata Connessione con Client: %s\n",
  6. inet_ntoa(Client_addr.sin_addr));


La funzione accept è definita sia in ambiente windows che in ambiente linux ed il suo prototipo è il seguente

  1. int accept(int sockfd, struct sockaddr *clientaddr, socklen_t *addrlen)

dove con sockfd si indica la socket su cui il server ha accettato la connessione, con clientaddr si indica l’indirizzo del client che ha effettuato la richiesta di connessione che è stata accettata, con addrlen si indica la lunghezza di questo indirizzo.
La funzione restituisce un socket descriptor relativo ad una nuova socket creata in seguito all’accettazione della connessione. Sarà su questa nuova socket che avverrà la comunicazione tra il server e il client che ha fatto la richiesta di connessione. Sulla socket originale il server continua a restare in ascolto per nuove richieste di connessione dei client.
Se al momento dell’esecuzione della istruzione, nessun client si è collegato, la funzione mette in attesa il processo. In caso di errore viene restituito il valore -1. Ritorna un numero di socket positivo in caso di successo oppure -1 se c’è errore; in tal caso errno può assumere gli stessi valori visti nel caso di listen().

Richiesta di Connessione del Client

Il client per potersi connettere con il server deve creare anch’esso una socket. Una volta creata la socket il client può avanzare una richiesta di connessione al server.

Figura 5.

La richiesta di connessione viene effettuata dal client tramite la funzione connect.

  1. addr.sin_family = AF_INET;
  2. addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  3. addr.sin_port = htons(port);
  4.  
  5. clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  6. connect(clientsocket, (LPSOCKADDR)&addr, sizeof(addr));

Non è necessario eseguire una chiama a bind prima di connect, il sistema operativo sceglierà automaticamente quale indirizzo IP e quale porta locali utilizzare per il collegamento. In caso di esito positivo della funzione connect, la connessione è completata e i processi possono comunicare.

La funzione connect è definita sia in ambiente windows che in ambiente linux ed il suo prototipo è il seguente

  1. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

dove con sockfd si indica l’identificativo del socket, con addr si indica l’indirizzo del server con cui ci si vuole connettere, e con addrlen si indica la lunghezza di questo indirizzo.
Ritorna 0 in caso di successo e -1 se c’è un errore, nel qual caso errno assume i valori del codice che indica l’errore.
La struttura sockaddr deve necessariamente contenere l’indirizzo IP e la porta del server a cui ci si vuole connettere.

Comunicazioni Client Server

Una volta stabilita la connessione tra client e server questi potranno comunicare tra loro, uno invierà dei dati che l’altro riceverà e viceversa.

Figura 6.

Per inviare e ricevere dati si usano le due funzioni send e recv. Il client invierà un messaggio al server, tramite la funzione send, indicando la socket tramite la quale si svolge la comunicazione con quel server, il messaggio da inviare, la dimensione del messaggio, in byte, ed il valore del flag, di solito posto a zero.

  1. send(clientsocket, messaggio, sizeof(messaggio), 0);

Il server riceverà il messaggio, tramite la funzione recv, indicando la socket tramite la quale si svolge la comunicazione con quel client, la variabile in cui porre il messaggio in arrivo, la dimensione, in byte, del messaggio da leggere, ed il valore del flag, di solito posto a zero.

  1. recv(remoteSocket, buffer, sizeof(buffer), 0);


La funzione send è definita sia in ambiente windows che in ambiente linux ed il suo prototipo è il seguente

  1. int send (int sock, char *buffer, int len, int flags)

dove con sock si indica la socket attraverso cui instradare il messaggio che si trova nella stringa buffer e la cui dimensione in byte è len.

La funzione recv è definita sia in ambiente windows che in ambiente linux ed il suo prototipo è il seguente

  1. int recv (int sock, char *buffer, int len, int flags)

dove con sock si indica la socket da cui si leggerà il messaggio in arrivo che sarà posto nella stringa buffer e la cui dimensione in byte è len.

Chiusura della Connessione

Una volta conclusa la comunicazione viene chiuso il canale di connessione costituito dalla socket.

Figura 7.

Per farlo si utilizza, sia nel client che nel server, la funzione close

  1. close(ssocket);
  2. WSACleanup();

La funzione close è definita sia in ambiente windows che in ambiente linux ed il suo prototipo è il seguente

  1. int close(int sock);

dove con sock si indica la socket che deve essere chiusa. Restituisce 0 in caso di successo o -1 in caso di errore.
I dati ancora presenti in coda vengono comunque inviati prima che della chiusura della socket.
Pagina 2 di 3
Prec 1 2 3 Succ