diff options
author | 2021-12-27 15:58:27 +0100 | |
---|---|---|
committer | 2021-12-27 15:58:27 +0100 | |
commit | bd13286d87e6273db0f55e8fd95ba3730171def4 (patch) | |
tree | e577625a7afbe553925c8c99e2c50ae302a32362 /articles/2009/20090602_socket_pt1.rhtml | |
download | website-bd13286d87e6273db0f55e8fd95ba3730171def4.tar.gz website-bd13286d87e6273db0f55e8fd95ba3730171def4.zip |
Initial commit
Diffstat (limited to 'articles/2009/20090602_socket_pt1.rhtml')
-rw-r--r-- | articles/2009/20090602_socket_pt1.rhtml | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/articles/2009/20090602_socket_pt1.rhtml b/articles/2009/20090602_socket_pt1.rhtml new file mode 100644 index 0000000..754a18e --- /dev/null +++ b/articles/2009/20090602_socket_pt1.rhtml @@ -0,0 +1,285 @@ +<div class="article"> + +<h1>Programmare con le socket (Parte 1)</h1> +<p class="date-published">Alessandro Iezzi, 2 giugno 2009</p> + + +<h2>Introduzione</h2> +<p>La <em>socket</em> (in inglese “<em>presa</em>“) è una + particolare astrazione software che permette + ai processi messi in comunicazione di inviare e ricevere dati. Le socket sono nate intorno agli anni ’80, + il primo kernel a implementarle fu BSD 4.2 nel 1983.</p> + + +<h2>Client/Server</h2> +<p>La struttura base di funzionamento delle socket è di tipo <em>Client/Server</em>. Supponiamo di avere +due processi p1 e p2. Il processo p2 ha bisogno del processo p1 per eseguire un determinato compito. Il processo p1 +offrirà al processo p2 tale servizio, esso perciò sarà il “Servente” ovvero il Server. Il processo p2 che richiede +il servizio sarà dunque il “Cliente”, ovvero il Client.</p> + + +<h2>Tipologie di socket</h2> +<p>Esistono quattro tipologie di socket:</p> +<ol> + <li>Socket che utilizzano i protocolli ARPA di internet (come TCP e UDP).</li> + <li>Gli Unix Domain Socket. Queste socket vengono usate in ambienti POSIX per la comunicazione in locale dei processi.</li> + <li>Socket che utilizzano i protocolli di Xerox Network System.</li> + <li>L’ultima tipologia è quella che utilizza i protocolli della Internationa Standard Association (fa riferimento al modello ISO/OSI).</li> +</ol> +<p>In questo articolo andremo a vedere solo la prima tipologia.<span id="more-275"></span></p> + + + +<h2>Come programmare con le socket</h2> +<h3>Lato server</h3> +<ul> + <li>Si genera la socket e la si associa al processo (binding, in italiano “legare”).</li> + <li>Si mette in ascolto il server per delle nuove connessioni da parte dei client, specificandone il numero.</li> + <li>A questo punto si accettano le connessioni e avviene la comunicazione.</li> +</ul> +<h3>Lato client</h3> +<ul> + <li>Si genera anche qui la socket (in questo caso si definisce l’indirizzo ip del server) e la si associa al processo.</li> + <li>Si effettua una connessione al server, se viene accettata può incominciare la comunicazione.</li> +</ul> +<p>Al termine della sessione di comunicazione vengono chiuse tutte le socket aperte ed i vari stream (nel caso di invii di file).</p> + + +<h2>Le funzioni da utilizzare</h2> +<h3>Funzione Socket</h3> +<p>La prima funzione che si va ad analizzare è:</p> +<pre>int socket(int domain, int type, >int protocol);</pre> +<p>La funzione restituisce un intero che ne identifica la socket. Questo intero viene chiamato <em>socket descriptor</em>. La funzione +richiede tre argomenti:</p> +<h4>Argomento: <em>domain</em></h4> +<p>Questo parametro serve a identificare quale tipo di famiglia di protocolli si vuole utilizzare (vedi, "Tipologie di socket").</p> +<ul> + <li>AF_INET: Protocolli ARPA di internet.</li> + <li>AF_UNIX: Protocolli interni di sistemi POSIX.</li> + <li>AF_NS: Protocolli di Xerox Network System.</li> + <li>AF_ISO: Protocolli della International Standard Association.</li> +</ul> +<p>Il prefisso AF sta per address family. Esistono anche un altro gruppo con prefisso PF. L’uso dei due prefissi è indifferente.</p> +<h4>Argomento: <em>type</em></h4> +<p>Questo parametro serve a specificare quale tipo di connessione deve essere stabilita. Può assumere i seguenti valori:</p> +<ul> + <li>SOCK_STREAM: Connessione sequenzia (TCP)</li> + <li> SOCK_DGRAM: Connessione trmite datagramma (UDP)</li> + <li>SOCK_RAW: Connessione tramite protocollo IP</li> +</ul> +<p>Esistono anche altri due tipi che non si vedranno.</p> +<h4>Argomento: <em>protocol</em></h4> +<p>Serve a specificare quale protocollo usare. Le possibili opzioni sono:</p> +<ul> + <li>IPPROTO_TCP: Protocollo TCP.</li> + <li>IPPROTO_UDP: Protocollo UDP.</li> + <li>IPPROTO_ICMP: Protocollo ICMP.</li> + <li>IPPROTO_RAW: Protocollo IP.</li> + <li>0: Il sistema sceglie il protocollo adatto, in base alla coppia degli argomenti domain e type. Infatti, AF_INET e SOCK_STREAM + identificano un protocollo TCP (IPPROTO_TCP), AF_INET e SOCK_DGRAM identificano un protocollo UDP (IPPROTO_UDP).</li> +</ul> +<h3>Funzione Bind</h3> +<p>Questa funzione permette di associare una socket ad un processo. La sua sintassi è la seguente:</p> +<pre>int bind(int sd, struct sockaddr *my_addr, int addr_lenght);</pre> +<p>Come si può notare richiede 3 argomenti:</p> +<h4>Argomento: <em>sd</em></h4> +<p>Ossia il <em>socket descriptor</em> ottenuto dalla funzione socket.</p> +<h4>Argomento: <em>*my_addr</em></h4> +<p>È una struttura che contiene informazioni come l’indirizzo al quale si vuole connettere, la famiglia a cui appartiene (AF_xxx) e +la porta. Tramite casting viene passata una struttura sockaddr_in:</p> +<pre>struct sockaddr_in { + short int sin_family; /* famiglia AF_xxx */ + unsigned short int sin_port; /* numero di porta */ + struct in_addr sin_addr; /* l'indirizzo al quale ci si connette (dipende dal protocollo) */ + char sin_zero[8] /* non usato */ +};</pre> +<p>Questa struttura attraverso il casting viene trasformata in quest’altra struttura:</p> +<pre>struct sockaddr { + uint8_t sa_len; + sa_family_t sa_family; /* famiglia AF_xxx */ + char sa_data[14]; /* l'indirizzo al quale ci si connette (dipende dal protocollo) */ +};</pre> +<h4>Argomento: <em>addr_lenght</em></h4> +<p>È la dimensione della struttura passata come secondo argomento. La dimensione della struttura si ricava utilizzando la funzione sizeof.</p> +<h3>Funzione Listen</h3> +<pre>int listen(int sd, int queue);</pre> +<p>Una volta creata la socket e associata al processo, quest’ultimo viene messo in ascolto per eventuali connessioni da parte dei client. Accetta solo due argomenti:</p> +<h4>Argomento: <em>sd</em></h4> +<p>Il <em>socket descriptor</em> generato dalla funzione socket</p> +<h4>Argomento: <em>queue</em></h4> +<p>Il numero di connessioni massime che ammette il processo.</p> +<h3>Funzione Accept</h3> +<p>Questa funzione consente di accettare le connessioni da parte dei client. Una volta messo in ascolto il processo viene eseguita questa +funzione che genera un socket descriptor associato al client connesso.</p> +<pre>int accept(int sd, struct sockaddr *client_addr, int client_addr_lenght);</pre> +<p>Gli argomenti che vengono richiesti sono:</p> +<h4>Argomento: <em>sd</em></h4> +<p>Il solito socket descriptor generato dalla funzione socket</p> +<h4>Argomento: <em>client_addr</em></h4> +<p>Una struttura che conterrà alcune informazioni del client. Come quelle viste in precedenza nella funzione bind.</p> +<h4>Argomento: <em>client_addr_lenght</em></h4> +<p>La dimensione della struttura che si passa come secondo argomento.</p> +<h3>Funzione Connect</h3> +<p>Consente ad un client di connettersi ad un server.</p> +<pre>int connect(int sdc, sockaddr *server_addr, int server_addr_lenght);</pre> +<p>Richiede tre argomenti:</p> +<h4>Argomento: <em>sdc</em></h4> +<p>È il socket descriptor generato dalla funzione socket richiamata all’interno del client.</p> +<h4>Argomento: <em>server_addr</em></h4> +<p>È la struttura che conterrà l’indirizzo a cui connettersi e la porta del server.</p> +<h4>Argomento: <em>server_addr_lenght</em></h4> +<p>È la lunghezza della struttura passato come secondo argomento.</p> +<h3>Le funzioni Recv e Send</h3> +<p>Vengono usate sia dal server che dal client per inviare informazioni:</p> +<pre>int recv(int sd, void *msg, int msg_lenght, int flag);</pre> +<pre>int send(int sd, void *msg, int msg_lenght, int flag);</pre> +<p>Richiedono tre argomenti:</p> +<h4>Argomento: <em>sd</em></h4> +<p>Il <em>socket descriptor;</em></p> +<h4>Argomento: <em>msg</em></h4> +<p>Per quanto riguarda la funzione recv sono i dati da ricevere, quindi è la variabile nella quale verrano memorizzati.<br> +Per quanto riguarda la funzione send sono i dati da inviare.</p> +<h4>Argomento: <em>flag</em></h4> +<p>Generalmente è posto a 0. Le opzioni possibili non saranno trattate in questo articolo.</p> + + + +<h2>Esempio</h2> +<p>Di seguito sono mostrati i codici sorgente di un server e di un client. Basta rinominarli rispettivamente in <em>server.c</em> e +<em>client.c</em>. Da terminale basta dare i seguenti comandi:</p> +<pre>$ gcc server.c -o server</pre> +<pre>$ gcc client.c -o client</pre> +<p>Ecco i sorgenti:</p> +<h6><span style="color: #3366ff;"><strong>server.c</strong></span></h6> +<pre lang="c">/* + * File: server.c + * Autore: Alessandro Iezzi + * Server d'esempio + */ +#include \<stdio.h> +#include \<string.h> +#include \<sys/types.h> +#include \<sys/socket.h> +#include \<netinet/in.h> +#include \<netdb.h> +#include \<stdlib.h> +/*#include \<arpa/inet.h>*/ /* Se si vuole visualizzare l'indirizzo ip del client, decommentare questa riga */ + +#define MAX 8192 /* in bytes, 8KB */ + +int main() +{ + char buff[MAX]; /* dati di invio e ricezione */ + struct sockaddr_in server_addr; /* indirizzo del server */ + struct sockaddr_in client_addr; /* indirizzo del client */ + int sd_server, sd_client; /* i socket descriptor usati per identificare server e client */ + + /* + * Creazione socket descriptor per il server. + * AF_INET + SOCK_STREAM --\> TCP, utilizzo del protocollo TCP (IPPROTO_TCP) + */ + if((sd_server = socket(AF_INET, SOCK_STREAM, 0)) \< 0) + printf("Errore nella creazione del server\\n"); + + /* + * Inseriamo nella struttura alcune informazioni + */ + server_addr.sin_family = AF_INET; /* la famiglia dei protocolli */ + server_addr.sin_port = htons(1745); /* la porta in ascolto */ + server_addr.sin_addr.s_addr = INADDR_ANY; /* dato che è un server bisogna associargli l'indirizzo della macchina su cui sta girando */ + + /* + * Assegnazione del processo alla socket tramite la funzione BIND + */ + if(bind(sd_server, (struct sockaddr *)\&server_addr, sizeof(server_addr)) \< 0) + printf("Errore di binding\\n"); + + /* + * Si mette in ascolto con un massimo di 20 connessioni + */ + listen (sd_server, 20); + + /* + * Essendo un server monothreading, accetterà una sola connessione per volta + */ + int address_size = sizeof(client_addr); /* dimensione della struttura client_addr */ + + /* Con opportune modifiche si potrebbe vedere anche l'ip del client */ + if((sd_client = accept(sd_server, (struct sockaddr *)\&client_addr, \&address_size)) \< 0) + printf("Errore nella chiamata accept\\n"); + + /* si ricevono i dati dal client */ + recv(sd_client, buff, sizeof(buff), 0); + printf("Dati ricevuti: %s\\n", buff); + + /* Decommentare queste due righe per visualizzare l'indirizzo ip del client */ + /* + char *ip_address = inet_ntoa(client_addr.sin_addr); + printf("IP del client: %s\\n", ip_address); + */ + + /* si spedisce un messaggio */ + strcpy(buff, "Tutto OK!"); + send(sd_client, buff, strlen(buff), 0); + + /* chiusura del socket descriptor */ + close(sd_client); + close(sd_server); + + return EXIT_SUCCESS; +}</code></pre> + +<h6><span style="color: #3366ff;"><strong>client.c</strong></span></h6> +<pre lang="c">/* + * File: client.c + * Autore: Alessandro Iezzi + * Client d'esempio + */ +#include \<stdio.h> +#include \<string.h> +#include \<sys/types.h> +#include \<sys/socket.h> +#include \<netdb.h> +#include \<netinet/in.h> +#include \<stdlib.h> + +#define MAX 8192 /* in bytes, 8KB */ + +int main() { + int sd; /* Il socket descriptor del client */ + struct sockaddr_in server_addr; /* l'indirizzo del server */ + char buff[MAX]; /* dati di invio e ricezione */ + + /* Utilizzando la struttura hostent si definisce l'indirizzo del server */ + struct hostent *hp; + hp = gethostbyname("127.0.0.1"); + + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(1745); + + /* successivamente viene memorizzato nella struttura server_addr */ + server_addr.sin_addr.s_addr = ((struct in_addr*)(hp-\>h_addr)) -\> s_addr; + + /* Viene creato il socket descriptor */ + if((sd = socket(AF_INET, SOCK_STREAM, 0)) \< 0) + printf("Errore nella creazione della socket\\n"); + + /* Viene connesso al server */ + if(connect(sd, (struct sockaddr *)\&server_addr, sizeof(server_addr)) \< 0) + printf("Errore di connessione al server\\n"); + + /* Si inviano alcuni dati */ + send(sd, "Dati inviati dal client", strlen("Dati inviati dal client"), 0); + + /* Si riceve la risposta */ + recv(sd, buff, sizeof(buff), 0); + printf("Risposta del server: %s\\n", buff); + + close(sd); + + return EXIT_SUCCESS; +}</pre> + + + +</div> |