1236 lines
104 KiB
HTML
1236 lines
104 KiB
HTML
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>
|
|
Scrivere web server - v0.2
|
|
</title>
|
|
<link rel="stylesheet" href="style.css" media="screen" />
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<a href="../index.html"><< Home</a>
|
|
<article>
|
|
<h1 id="scrivere-un-server-tcp">Scrivere un server TCP</h1>
|
|
|
|
<h2 id="indice">Indice</h2>
|
|
<nav>
|
|
<ol>
|
|
<li>
|
|
<a href="#scrivere-un-server-tcp">
|
|
Scrivere un server TCP
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#indice">
|
|
Indice
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#introduzione">
|
|
Introduzione
|
|
</a>
|
|
<ol>
|
|
<li>
|
|
<a href="#cos-e-tcp">
|
|
Cos'è TCP?
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#come-funziona">
|
|
Come funziona?
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#cosa-realizzeremo">
|
|
Cosa realizzeremo
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#come-lo-realizzeremo">
|
|
Come lo realizzeremo: I socket
|
|
</a>
|
|
</li>
|
|
</ol>
|
|
</li>
|
|
<li>
|
|
<a href="#socket">
|
|
Socket
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#configurazione-del-socket-per-il-server">
|
|
Configurazione del socket per il server
|
|
</a>
|
|
<ol>
|
|
<li>
|
|
<a href="#bind">Bind</a>
|
|
</li>
|
|
<li>
|
|
<a href="#listen">Listen</a>
|
|
</li>
|
|
<li>
|
|
<a href="#accept">Accept</a>
|
|
</li>
|
|
</ol>
|
|
</li>
|
|
<li>
|
|
<a href="#configurazione-del-socket-per-il-client">
|
|
Configurazione del socket per il client
|
|
</a>
|
|
<ol>
|
|
<li>
|
|
<a href="#connect">Connect</a>
|
|
</li>
|
|
</ol>
|
|
</li>
|
|
<li>
|
|
<a href="#la-business-logic">
|
|
La business logic
|
|
</a>
|
|
<ol>
|
|
<li>
|
|
<a href="#send">Send</a>
|
|
</li>
|
|
<li>
|
|
<a href="#recv">Recv</a>
|
|
</li>
|
|
</ol>
|
|
</li>
|
|
<li>
|
|
<a href="#la-business-logic-del-client">
|
|
La business logic del client
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#la-business-logic-del-server">
|
|
La business logic del server
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="#riferimenti">Riferimenti</a>
|
|
</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<h2 id="introduzione">Introduzione</h2>
|
|
<p>
|
|
In questo post spiegherò come scrivere un server TCP e, per mostrarne il funzionamento, il rispettico client.
|
|
</p>
|
|
|
|
<h3 id="cos-e-tcp">Cos'è TCP?</h3>
|
|
<p>
|
|
TCP è un protocollo di rete, ossia un protocollo che permette lo scambio di messaggi tra computer. Più nel dettaglio quello che ci permette di fare è collegare virtualmente due computer e trasferire un insieme di byte da uno all'altro. Il collegamento è virtuale perchè in realtà i computer si suppongono già fisicamente collegati mediante un insieme di router, tuttavia è solo quando entrambi manifestano l'intento di voler comunicare che questo può avvenire. Quindi per "virtuale" intendo dire che il collegamento è un'astrazione perchè di fatto i computer sono sempre stati collegati.
|
|
</p>
|
|
|
|
<h3 id="come-funziona">Come funziona?</h3>
|
|
<p>
|
|
Per poter comunicare usando TCP, si deve prima decidere chi dei due nodi è il server e chi è il client. Il server è quello che si mette in ascolto per la connessione, mentre il client è colui che decide quando comunicazione deve cominciare. È da notare che la differenza tra server e client non è legata a chi invia e chi riceve informazioni, è solo un modo per gestire il primo collegamento. Se non ci fosse questa distinzione di ruoli allora sarebbe quasi impossibile creare connessioni TCP! Supponiamo che i ruoli siano simmetrici e che la connessione si avvii quando entrambi allo stesso tempo decidono di aprirla: quali sono le probabilità che due utenti ed i rispettivi computer si colleghino allo stesso tempo? Per farlo in modo preciso sarebbe richiesto qualche tipo di sincronizzazione, ma questa implicherebbe una comunicazione, tuttavia per ipotesi i computer non stanno ancora comunicando!! Un analogia che potrebbe mostrare questo problema sotto un'altra luce è quella dei telefoni cellulari. In effetti ogni volta che abbiamo addosso il cellulare ci stiamo comportando da server, perchè in ogni istante siamo aperti a ricevere telefonate. Dall'altra parte un nostro amico che ci chiama si comporta da client poichè è lui che decide di avviare la comunicazione. Rimuovere questi due ruoli comporterebbe che per parlare a telefono sia noi che il nostro amico dovremmo premere il tasto verde di chiamata allo stesso tempo.
|
|
</p>
|
|
<p>
|
|
Per il protocollo TCP ogni computer su una rete è caratterizzato da un identificativo univoco chiamato indirizzo IP. In più su ciascun PC ogni programma è caratterizzato da un ulteriore identificativo chiamato porta univoco localmente. Naturalmente due computer per parlarsi hanno bisogno dei rispettivi IP, tuttavia quelli che parlano fra di loro sono <i>i programmi</i> in esecuzione sui due PC, quindi c'è bisogno della porta come ulteriore fattore discriminante. Per questo motivo per avviare la comunicazione il client deve conoscere il "nome" del server, cioè la coppia indirizzo-porta. Quando il client contatterà il server, automaticamente questo verrà a conoscenza degli identificativi del client. Questo comporta che il client debba conoscere a priori l'indirizzo e la porta del server! Questo però nella pratica rappresenta un vincolo molto poco limitante.
|
|
</p>
|
|
<p>
|
|
Gli indirizzi IP sono stringhe di 32 cifre binarie (bit). Le combinazioni di 0 ed 1 (i bit) che si possono fare con una stringa di questa lunghezza sono circa 4 miliardi, per cui questo è il limite di computer che si possono avere su una rete. Per quanto riguarda le porte, queste sono stringhe binarie di 16 cifre. Le permutazioni che invece si possono ottenere in questo caso sono 65536. Quando si programma di solito le porte, dato che assumono valori piccoli (rispetto agli indirizzi IP), vengono riportate come numero (cioè in base 10 invece che base 2). Per quanto riguarda gli indirizzi IP invece, riportarle come numeri risulterebbe scomodo dato che è difficile distinguere numeri molto grandi ad occhio. Per questo motivo si usa la notazione <i>dot-decimal</i>, per la quale la stringa di 32 bit viene divisa in 4 stringhe da 8 e ciascuna sotto-stringa viene rappresentata in base 10 separata dalle altre con un puntino. Indirizzi IP espressi usando questa notazione sono molto comuni e chiunque abbia usato un computer ne avrà visto uno. Un esempio di indirizzo IP in notazione dot-decimal è <code>192.168.0.1</code>. Dato che una stringa di 8 bit ha 256 permutazioni, i 4 numeri nella notazione dot-decimal possono solo assumere valori da 0 a 255 (i numeri da 0 a 255 sono in totale 256, cioè di conta pure lo 0).
|
|
</p>
|
|
<p>
|
|
Una volta avviata la comunicazione, i due processi mandano messaggi ed attendono risposte, sincronizzandosi, effettuando elaborazioni e producendo risultati grazie a questa cooperazione. Una volta conclusa la comunicazione, entrambi i processi possono chiudere la connessione.
|
|
</p>
|
|
<p>
|
|
Queste sono solo parole, noi dobbiamo farlo in codice tutto questo!! Me non temete! Ogni passo sarà accuratamente ed amorevolmente documentato :^) <3
|
|
</p>
|
|
|
|
<h3 id="cosa-realizzeremo">Cosa realizzeremo</h3>
|
|
<p>
|
|
In verità dire che faremo un server ed un client TCP non è una descrizione esaustiva. TCP descrive solo il metodo che i programmi useranno per comunicare, ma non cosa si diranno! In particolare quel che faremo è un client che invia un messaggio al server, che leggerà il messaggio, ne convertirà maiuscole in minuscole e viceversa, e poi rimanderà la versione elaborata del messaggio.
|
|
</p>
|
|
|
|
<h3 id="come-lo-realizzeremo">Come lo realizzeremo: I socket</h3>
|
|
<p>
|
|
Per realizzare un'applicazione del genere, dovremo fare quella che viene chiamata <i>socket programming</i>, cioè la programmazione che usa i "socket".
|
|
</p>
|
|
<p>
|
|
Un socket è una struttura dati del kernel della quale possiamo chiamare dei metodi per effettuare delle comunicazioni sulla rete. È il modo in cui il sistema operativo astrae le funzionalità più crude dell'hardware. Il tema dell'astrazione dell'hardare è molto vasto, ma per i nostri scopi ci basta questo accenno.
|
|
</p>
|
|
<p>
|
|
Il client avrà una struttura del tipo
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-identifier c2h-fdeclname">main</span>()</td></tr>
|
|
<tr><td>2</td><td>{</td></tr>
|
|
<tr><td>3</td><td> <span class="c2h-identifier">address</span> <span class="c2h-operator">=</span> <span class="c2h-val-flt">127.0</span>.<span class="c2h-val-flt">0.1</span></td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-identifier">port</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">8080</span></td></tr>
|
|
<tr><td>5</td><td> <span class="c2h-identifier">socket</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">create_tcp_socket</span>()</td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-identifier c2h-fcallname">connect</span>(<span class="c2h-identifier">socket</span>, <span class="c2h-identifier">address</span>, <span class="c2h-identifier">port</span>)</td></tr>
|
|
<tr><td>8</td><td></td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-identifier">messaggio</span> <span class="c2h-operator">=</span> <span class="c2h-val-str">"Ossimoro"</span></td></tr>
|
|
<tr><td>10</td><td></td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-identifier c2h-fcallname">send</span>(<span class="c2h-identifier">socket</span>, <span class="c2h-identifier">messaggio</span>)</td></tr>
|
|
<tr><td>12</td><td></td></tr>
|
|
<tr><td>13</td><td> <span class="c2h-identifier">messaggio</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">receive</span>(<span class="c2h-identifier">socket</span>)</td></tr>
|
|
<tr><td>14</td><td></td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-identifier c2h-fcallname">print</span>(<span class="c2h-val-char">'Il server ha risposto con: '</span> <span class="c2h-operator">+</span> <span class="c2h-identifier">messaggio</span>)</td></tr>
|
|
<tr><td>16</td><td></td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">socket</span>)</td></tr>
|
|
<tr><td>18</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
mentre il server:
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-identifier c2h-fdeclname">main</span>() </td></tr>
|
|
<tr><td>2</td><td>{</td></tr>
|
|
<tr><td>3</td><td> <span class="c2h-identifier">port</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">8080</span></td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-identifier">socket</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">create_tcp_socket</span>()</td></tr>
|
|
<tr><td>5</td><td> </td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-identifier c2h-fcallname">listen</span>(<span class="c2h-identifier">socket</span>, <span class="c2h-identifier">port</span>)</td></tr>
|
|
<tr><td>7</td><td></td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-identifier">loop</span> {</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-identifier">socket2</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">accept</span>(<span class="c2h-identifier">socket</span>)</td></tr>
|
|
<tr><td>10</td><td></td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-identifier">messaggio</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">read</span>(<span class="c2h-identifier">socket2</span>)</td></tr>
|
|
<tr><td>12</td><td></td></tr>
|
|
<tr><td>13</td><td> <span class="c2h-identifier">messaggio</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">convert_case</span>(<span class="c2h-identifier">messaggio</span>)</td></tr>
|
|
<tr><td>14</td><td></td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-identifier c2h-fcallname">send</span>(<span class="c2h-identifier">socket2</span>, <span class="c2h-identifier">messaggio</span>)</td></tr>
|
|
<tr><td>16</td><td></td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">socket2</span>)</td></tr>
|
|
<tr><td>18</td><td> }</td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">socket</span>)</td></tr>
|
|
<tr><td>20</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 id="socket">Socket</h3>
|
|
<p>
|
|
I socket possono svolgere tante funzioni, quindi quando ne creiamo uno dobbiamo specificare le modalità di utilizzo. La funzione che permette di creare un socket è
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">socket</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">domain</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">type</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">protocol</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p class="nota">
|
|
Con questa notazione <i>include + prototipo della funzione</i> intendo dire che la libreria inclusa definisce la funzione sotto.
|
|
</p>
|
|
|
|
<p>
|
|
Il modo in cui si usa questa funzione è un po' caotico. Questa cosa è dovuta al fatto che quando ne fu progettata l'interfaccia, si cercò di prevedere gli sviluppi futuri, che poi non avvennero mai.
|
|
</p>
|
|
|
|
<p>
|
|
Il primo argomento <code>domain</code> specifica lo spazio degli indirizzi del protocollo che vogliamo usare. Il valore di questo parametro può essere specificato usando dei valori specificati in <code>sys/socket.h</code> come <code>AF_INET</code>, <code>AF_UNIT</code> oppure <code>AF_BLUETOOTH</code>. Il prefisso <code>AF_</code> sta per <i>Address Family</i>. Noi useremo solo <code>AF_INET</code>, in cui <code>INET</code> sta, banalmente, per <i>internet</i>.
|
|
</p>
|
|
|
|
<p>
|
|
Il secondo argomento <code>type</code> specifica quello che comunemente indichiamo col nome di protocollo, cioè TCP nel nostro caso. Il valore che dobbiamo fornire per TCP è <code>SOCK_STREAM</code>. Altri esempi di protocolli che possiamo specificare sono UDP (<code>SOCK_DGRAM</code>) o l'assenza di protocollo (<code>SOCK_RAW</code>).
|
|
</p>
|
|
|
|
<p>
|
|
Il terzo valore <code>protocol</code> è superfluo. Nel caso di TCP può sempre essere zero.
|
|
</p>
|
|
|
|
<p>
|
|
La funzione <code>socket</code> ritorna quel che è comunemente chiamato <i>descrittore</i> del socket, cioè un numero intero positivo che descrive inequivocabilmente il nostro socket all'interno del kernel. Ogni volta che vorremo operare sul socket, forniremo alle funzione questo descrittore ed il sistema operativo saprà su quale socket operare. Se la creazione della struttura fallisce, allora un valore negativo è ritornato.
|
|
</p>
|
|
|
|
<p>
|
|
In sintesi, per creare un socket TCP faremo una cosa del tipo:
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>3</td><td></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>5</td><td>{</td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>10</td><td> }</td></tr>
|
|
<tr><td>11</td><td></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>13</td><td></td></tr>
|
|
<tr><td>14</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>16</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p class="nota">
|
|
Su linux ed i sistemi unix in generale, le risorse sono spesso astratte sotto il concetto di file. I socket sarebbero un tipo speciale di file. Per questo motivo il descrittore dei socket è anche chiamato file descriptor (e da questo il nome <code>fd</code> della variabile)
|
|
</p>
|
|
|
|
<p>
|
|
Sia nel caso del client che del server l'apertura e chiusura del socket sono uguali. Quello che cambia è la loro configurazione e poi connessione. Una volta avviata invece il codice torna simmetrico, nel senso che i due programmi possono ricevere ed inviare dati allo stesso modo.
|
|
</p>
|
|
|
|
<h2 id="configurazione-del-socket-per-il-server">
|
|
Configurazione del socket per il server
|
|
</h2>
|
|
<p>
|
|
Quel che il server dovrà fare dopo aver creato il socket è assegnargli un indirizzo IP tra quelli che ha a disposizione (potrebbe evere più di una interfaccia di rete!). Questo si fa usando la funzione <code>bind</code>, infatti "bind" si potrebbe tradurre dall'inglese all'italiano con "legare" (il socket è legato all'indirizzo). Una volta fatto questo è possibile attendere la connessione del client usando la funzione <code>listen</code>, che "ascolta" per connessioni ("listen" è la parola inglese che corrisponde ad "ascoltare" in italiano). Per accettare le connessioni dopo aver fatto <code>listen</code>, il server userà la funzione <code>accept</code>, che restituirà un nuovo socket relativo alla particolare connessione appena creata.
|
|
</p>
|
|
|
|
<h3 id="bind">Bind</h3>
|
|
<p>
|
|
La funzione <code>bind</code> è la parte più difficile della guida, quindi una volta fatta quella sarà tutto in discesa! L'interfaccia della funzione è questa qui:
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">bind</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span> <span class="c2h-operator">*</span><span class="c2h-identifier">addr</span>, <span class="c2h-identifier">socklen_t</span> <span class="c2h-identifier">addrlen</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
Ad un livello più alto, quello che fa <code>bind</code> è prendere un buffer contenente un indirizzo ed associarlo ad un socket. Dato che questa funzione si può usare per più protocolli con spazi di indirizzi diversi, fa il minimo numero di assunzioni su come sia fatto un indirizzo. Infatti, la struttura che rappresente un indirizzo generico per <code>bind</code> è questa qui
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-typedef">typedef</span> <span class="c2h-kword c2h-kword-unsigned">unsigned</span> <span class="c2h-kword c2h-kword-short">short</span> <span class="c2h-identifier">sa_family_t</span>;</td></tr>
|
|
<tr><td>4</td><td><span class="c2h-comment">// [sa_family_t] contiene i valori [AF_*]</span></td></tr>
|
|
<tr><td>5</td><td></td></tr>
|
|
<tr><td>6</td><td><span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span> {</td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-identifier">sa_family_t</span> <span class="c2h-identifier">sa_family</span>;</td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-identifier">sa_data</span>[<span class="c2h-val-int">14</span>];</td></tr>
|
|
<tr><td>9</td><td>};</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
in cui l'effettivo valore dell'indirizzo è un array di byte. Programmando in C, interpretare una struttura dati come array di caratteri corrisponde a dire "non mi interessa sapere come è strutturato questo dato, voglio solo poterlo spostare in giro". L'indirizzo effettivo poi potrebbe anche essere meno o più grande di <code>struct sockaddr</code>, da cui la necessita dell'argomento <code>addrlen</code> di <code>bind</code> in cui è possibile specificare la dimensione della regione puntata dall'argomento <code>addr</code>.
|
|
</p>
|
|
|
|
<p class="nota">
|
|
Se questa interfaccia ti sembra confusa, non temere! È quel che provano tutti. È il risultato di anni di standard e convenzioni che hanno avuto bisogno di essere sempre retrocompatibili.
|
|
</p>
|
|
|
|
<p>
|
|
Tuttavia, quando andremo a costruire l'indirizzo da fornire a <code>bind</code> non opereremo su una struttura del tipo <code>struct sockaddr</code> ma di tipo <code>struct sockaddr_in</code>, definita come
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> {</td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-kword c2h-kword-short">short</span> <span class="c2h-identifier">sin_family</span>;</td></tr>
|
|
<tr><td>5</td><td> <span class="c2h-kword c2h-kword-unsigned">unsigned</span> <span class="c2h-kword c2h-kword-short">short</span> <span class="c2h-identifier">sin_port</span>;</td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">in_addr</span> <span class="c2h-identifier">sin_addr</span>;</td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-identifier">sin_zero</span>[<span class="c2h-val-int">8</span>];</td></tr>
|
|
<tr><td>8</td><td>};</td></tr>
|
|
<tr><td>9</td><td></td></tr>
|
|
<tr><td>10</td><td><span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">in_addr</span> {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-kword c2h-kword-unsigned">unsigned</span> <span class="c2h-kword c2h-kword-long">long</span> <span class="c2h-identifier">s_addr</span>;</td></tr>
|
|
<tr><td>12</td><td>};</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
il cui layout è compatibile a <code>struct sockaddr</code> (entrambi hanno <code>fin_family</code> allo stesso posto e le loro dimensioni sono uguali, di 16 byte), ma in più il campi <code>sa_data</code> di <code>struct sockaddr</code> è esploso nei tre campo <code>sin_port</code>, <code>sin_addr</code> e <code>sin_zero</code>. Una volta configurata questa struttura potremo fornirla a <code>bind</code> dopo aver fatto un cast a <code>struct sockaddr</code>.
|
|
</p>
|
|
|
|
<p>
|
|
Il campo <code>sin_family</code> di <code>struct sockaddr</code> (e quindi di <code>struct sockaddr_in</code>) contiene il tipo di indirizzo, quindi uno di quegli identificativi che cominciano con <code>AF_</code>. Il valore di questo campo deve essere uguale al primo argomento di <code>socket</code>, cioè <code>AF_INET</code>. Del resto ha senso interpretare <code>struct sockaddr</code> come un <code>struct sockaddr_in</code> solo quando usiamo <code>AF_INET</code>. Il campo <code>sin_family</code> di un <code>struct sockaddr_in</code> sarà sempre <code>AF_INET</code>.
|
|
</p>
|
|
|
|
<p>
|
|
I campi <code>sin_port</code> e <code>sin_addr</code> (più in particolare <code>sin_addr.s_addr</code>) sono rispettivamente porta ed indirizzo, cioè gli stessi porta ed indirizzo di cui parlavamo nell'introduzione al TCP. Questi due campi dovranno avere l'ordinamento dei byte della rete (cioè big endian, nel caso del TCP), quindi se il nostro computer non ha lo stesso suo ordinamento (cioè è little endian) allora dovremo effettuare una conversione. Per effettuare questa conversione comodamente, possiamo usare le funzioni
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-identifier">uint32_t</span> <span class="c2h-identifier c2h-fdeclname">htonl</span>(<span class="c2h-identifier">uint32_t</span> <span class="c2h-identifier">hostlong</span>);</td></tr>
|
|
<tr><td>3</td><td><span class="c2h-identifier">uint16_t</span> <span class="c2h-identifier c2h-fdeclname">htons</span>(<span class="c2h-identifier">uint16_t</span> <span class="c2h-identifier">hostshort</span>);</td></tr>
|
|
<tr><td>4</td><td><span class="c2h-identifier">uint32_t</span> <span class="c2h-identifier c2h-fdeclname">ntohl</span>(<span class="c2h-identifier">uint32_t</span> <span class="c2h-identifier">netlong</span>);</td></tr>
|
|
<tr><td>5</td><td><span class="c2h-identifier">uint16_t</span> <span class="c2h-identifier c2h-fdeclname">ntohs</span>(<span class="c2h-identifier">uint16_t</span> <span class="c2h-identifier">netshort</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p>
|
|
Che trasformano interi da ordinamento dell'host (il nostro computer) a quello della rete o viceversa. Infatti "hton" ed "ntoh" stanno per "host to network" e "network to host". La "l" o "s" finali invece fanno riferimento al tipo di dato che convertono, cioè "long" o "short". Nel caso dell'indirizzo dovremo usare <code>htonl</code>, mentre nel caso della porta <code>htons</code>.
|
|
</p>
|
|
|
|
<p>
|
|
Invece di specificare un singolo indirizzo IP, possiamo usare la costante <code>INADDR_ANY</code>
|
|
definita in <code>arpa/inet.h</code> per effettuare il bind su tutti i possibili indirizzi disponibili.
|
|
</p>
|
|
|
|
<p>
|
|
L'ultimo campo <code>sin_zero</code> ha come unica funzione quella di far combaciare la dimensione delle strutture <code>sockaddr</code> e <code>sockaddr_in</code>. Infatti il nome stesso contiene la parola "zero" per indicare che questo campo dovrebbe solo errere posto uguale a zero.
|
|
</p>
|
|
|
|
<p>
|
|
Il valore di ritorno di <code>bind</code> è zero quando tutto è andato a buon fine ed un valore negativo quando ha fallito.
|
|
</p>
|
|
|
|
<p>
|
|
Detto questo, il binding del server sarà fatto in questo modo qui
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><string.h></span> <span class="c2h-comment">// memset</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>5</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>8</td><td>{</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td> </td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> <span class="c2h-identifier">addr</span>;</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_family</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">AF_INET</span>;</td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_port</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">htons</span>(<span class="c2h-val-int">8080</span>);</td></tr>
|
|
<tr><td>18</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_addr</span>.<span class="c2h-identifier">s_addr</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">INADDR_ANY</span>;</td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier c2h-fcallname">memset</span>(<span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>, <span class="c2h-val-int">0</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>));</td></tr>
|
|
<tr><td>20</td><td> </td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">bind</span>(<span class="c2h-identifier">fd</span>, (<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span><span class="c2h-operator">*</span>) <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>22</td><td> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span>))) {</td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-comment">// Il binding è fallito!!</span></td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>26</td><td> }</td></tr>
|
|
<tr><td>27</td><td></td></tr>
|
|
<tr><td>28</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>29</td><td></td></tr>
|
|
<tr><td>30</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>32</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
Decisamente più facile a farsi che a dirsi!!
|
|
</p>
|
|
|
|
<p class="nota">
|
|
Come per <code>AF_INET</code>, le altre famiglie di indirizzi rappresentano a loro modo il contenuto di <code>struct sockaddr</code>. Per questo motivo ciascuna definisce una propria struttura analoga a <code>struct sockaddr_in</code> per costruire i propri indirizzi. Ad esempio per <code>AF_INET6</code> (anche noto come IPv6) si usa <code>sockaddr_in6</code>, mentre per <code>AF_UNIX</code> (i socket unix) si usa <code>struct sockaddr_un</code>. Tuttavia queste strutture potrebbero non avere la stessa dimensione di <code>struct sockaddr</code>! Questo è dovuto al fatto che quando è stata progettata questa API originariamente si è supposto che 14 byte sarebbero bastati per ogni genere di indirizzo. Quando poi è risultato necessario avere indirizzi più grandi, si è preferito non cambiare la definizione di <code>struct sockaddr</code> per rimanere retrocompatibili. In ogni caso questa differenza di dimensione non crea problemi di correttezza nei programmi.
|
|
</p>
|
|
|
|
<h3 id="listen">Listen</h3>
|
|
<p>
|
|
Ultimo passo dell'inizializzazione è il <code>listen</code>ing, cioè effettivamente ascoltare per nuove connessioni. La funzione che dobbiamo usare è
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">listen</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">backlog</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p>
|
|
Il primo argomento <code>sockfd</code> naturalmente è il socket che vogliamo mettere in ascolto, mentre il secondo <code>backlog</code> corrisponde alla dimensione della coda di connessioni non ancora accettate. Il valore di ritorno, come per <code>bind</code>, è non-zero quando qualcosa va storto.
|
|
</p>
|
|
|
|
<p>
|
|
Ogni volta che una richiesta di connessione viene ricevuta mentre il programma è in ascolto, il kernel la mette in una coda. Il nostro programma dal lato suo prenderà elementi da quella coda usando la funzione <code>accept</code>. Il paramentro <code>backlog</code> fa riferimento alla dimensione di queste coda. Se una nuova connessione arriva ma la code è piena, sarà automaticamente rifiutata, per questo è importante scegliere accuratamente questo valore. Se questo parametro è troppo piccolo ed il programma non riesce ad accettare connessioni con una frequenza comparabile a quella con cui arrivano, allora delle connessioni saranno perse. Se invece lo scegliamo troppo grande, delle risorse saranno sprecate.
|
|
</p>
|
|
|
|
<p>
|
|
Naturalmente per condizioni di prova come la nostra ha poca importanza che valore usiamo. Anche scegliere <code>1</code> andrà bene, tuttavia portando questo programma in circostanze più realistiche, questo creerebbe problemi!
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><string.h></span> <span class="c2h-comment">// memset</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>5</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>8</td><td>{</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td> </td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> <span class="c2h-identifier">addr</span>;</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_family</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">AF_INET</span>;</td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_port</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">htons</span>(<span class="c2h-val-int">8080</span>);</td></tr>
|
|
<tr><td>18</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_addr</span>.<span class="c2h-identifier">s_addr</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">INADDR_ANY</span>;</td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier c2h-fcallname">memset</span>(<span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>, <span class="c2h-val-int">0</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>));</td></tr>
|
|
<tr><td>20</td><td> </td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">bind</span>(<span class="c2h-identifier">fd</span>, (<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span><span class="c2h-operator">*</span>) <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>22</td><td> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span>))) {</td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-comment">// Il binding è fallito!!</span></td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>26</td><td> }</td></tr>
|
|
<tr><td>27</td><td></td></tr>
|
|
<tr><td>28</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">listen</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-val-int">32</span>)) {</td></tr>
|
|
<tr><td>29</td><td> <span class="c2h-comment">// Non sono riuscito ad ascoltare!</span></td></tr>
|
|
<tr><td>30</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>32</td><td> }</td></tr>
|
|
<tr><td>33</td><td></td></tr>
|
|
<tr><td>34</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>35</td><td></td></tr>
|
|
<tr><td>36</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>37</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>38</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<h3 id="accept">Accept</h3>
|
|
<p>
|
|
Una volta completata la configurazione, il server potrà accettare nuove connessioni, usarle e poi chiuderle iterativamente. Come accennato prima, per accettare nuove richiesta si usa
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">accept</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span> <span class="c2h-operator">*</span><span class="c2h-identifier">restrict</span> <span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-identifier">socklen_t</span> <span class="c2h-operator">*</span><span class="c2h-identifier">restrict</span> <span class="c2h-identifier">addrlen</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
dove <code>sockfd</code> è il descrittore del socket messo in ascolto mentre <code>addr</code> e <code>addrlen</code> sono argomenti di output che restituiscono l'indirizzo del client in una struttura del tipo <code>struct sockaddr_in</code>. Se non siamo interessati all'indirizzo possiamo fornire <code>NULL</code> ai due argomenti finali. Il valore di ritorno invece è il descrittore di un nuovo socket dal quale si potranno leggere e scrivere byte. Una volta conclusa la comunicazione con questo client, questo socket potrà essere chiuso ed una nuova connessione accettata usando dinuovo <code>accept</code>. Naturalmente è anche possibile accettare più connessioni allo stesso tempo chiamando <code>accept</code> più volte in sequenza prima, ma questo ci costringerebbe a dover rendere decisamente più sofisticata la struttura del nostro programma.
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><string.h></span> <span class="c2h-comment">// memset</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>5</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>8</td><td>{</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td> </td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> <span class="c2h-identifier">addr</span>;</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_family</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">AF_INET</span>;</td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_port</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">htons</span>(<span class="c2h-val-int">8080</span>);</td></tr>
|
|
<tr><td>18</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_addr</span>.<span class="c2h-identifier">s_addr</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">INADDR_ANY</span>;</td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier c2h-fcallname">memset</span>(<span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>, <span class="c2h-val-int">0</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>));</td></tr>
|
|
<tr><td>20</td><td> </td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">bind</span>(<span class="c2h-identifier">fd</span>, (<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span><span class="c2h-operator">*</span>) <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>22</td><td> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span>))) {</td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-comment">// Il binding è fallito!!</span></td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>26</td><td> }</td></tr>
|
|
<tr><td>27</td><td></td></tr>
|
|
<tr><td>28</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">listen</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-val-int">32</span>)) {</td></tr>
|
|
<tr><td>29</td><td> <span class="c2h-comment">// Non sono riuscito ad ascoltare!</span></td></tr>
|
|
<tr><td>30</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>32</td><td> }</td></tr>
|
|
<tr><td>33</td><td></td></tr>
|
|
<tr><td>34</td><td> <span class="c2h-kword c2h-kword-while">while</span>(<span class="c2h-val-int">1</span>) {</td></tr>
|
|
<tr><td>35</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">clifd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">accept</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">NULL</span>, <span class="c2h-identifier">NULL</span>);</td></tr>
|
|
<tr><td>36</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">clifd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>)</td></tr>
|
|
<tr><td>37</td><td> <span class="c2h-kword c2h-kword-continue">continue</span>; <span class="c2h-comment">// Torna in cima al loop</span></td></tr>
|
|
<tr><td>38</td><td> </td></tr>
|
|
<tr><td>39</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>40</td><td></td></tr>
|
|
<tr><td>41</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">clifd</span>);</td></tr>
|
|
<tr><td>42</td><td> }</td></tr>
|
|
<tr><td>43</td><td></td></tr>
|
|
<tr><td>44</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>45</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>46</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<h2 id="configurazione-del-socket-per-il-client">
|
|
Configurazione del socket per il client
|
|
</h2>
|
|
|
|
<p>
|
|
La configurazione del client è più semplice del server. Come il server crea un socket TCP usando <code>socket</code>, poi però si connette al server chiamando la funzione <code>connect</code>. Se <code>connect</code> ha avuto buon fine, la connessione è stata stabilita ed è possibile comunicare.
|
|
</p>
|
|
|
|
<h3 id="connect">Connect</h3>
|
|
<p>
|
|
La funzione <code>connect</code> è definita in questo modo qui
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">connect</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span> <span class="c2h-operator">*</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-identifier">socklen_t</span> <span class="c2h-identifier">addrlen</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
Dove <code>sockfd</code> è il socket che vogliamo usare per effettuare la connessione (quello creato con <code>socket</code>) e la coppia <code>addr</code>/<code>addrlen</code> specifica l'indirizzo al quale vogliamo connetterci. Come per gli altri caso avremo <code>addr</code> pari ad un <code>struct sockaddr_in</code> e <code>addrlen</code> pari a <code>sizeof(struct sockaddr_in)</code>. Se la funzione fallisce, allora <code>-1</code> viene ritornato, altrimenti <code>0</code>.
|
|
</p>
|
|
<p>
|
|
L'inizializzazione del client quindi avrà una forma del genere
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><string.h></span> <span class="c2h-comment">// memset</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>5</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>8</td><td>{</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td> </td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> <span class="c2h-identifier">addr</span>;</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_family</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">AF_INET</span>;</td></tr>
|
|
<tr><td>17</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_port</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">htons</span>(<span class="c2h-val-int">8080</span>); <span class="c2h-comment">// Questa è la stessa del server!!</span></td></tr>
|
|
<tr><td>18</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_addr</span>.<span class="c2h-identifier">s_addr</span> <span class="c2h-operator">=</span> ???; <span class="c2h-comment">// Ma qui cosa ci và?!</span></td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier c2h-fcallname">memset</span>(<span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>, <span class="c2h-val-int">0</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>));</td></tr>
|
|
<tr><td>20</td><td> </td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">connect</span>(<span class="c2h-identifier">fd</span>, (<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span><span class="c2h-operator">*</span>) <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>22</td><td> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span>))) {</td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-comment">// La connessione è fallita!!</span></td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>26</td><td> }</td></tr>
|
|
<tr><td>27</td><td></td></tr>
|
|
<tr><td>28</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>29</td><td></td></tr>
|
|
<tr><td>30</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>32</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
L'indirizzo che forniamo a <code>connect</code> deve avere la stessa porta sulla quale il server ha fatto <code>bind</code> e come indirizzo IP anche quello sul quale ha fatto <code>bind</code>. Se il server ha fatto bind su più di un indirizzo IP usando <code>INADDR_ANY</code>, ci basta specificare uno di quelli a sua disposizione.
|
|
</p>
|
|
|
|
<p>
|
|
Normalmente gli indirizzi IP sono noti in rappresentazione dotted decimal. In tal caso per convertirli da tale rappresentazione ad un intero con i byte ordinati come piace alla rete, si può usare la funzione <code>inet_pton</code>:
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">inet_pton</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">af</span>, <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-operator">*</span><span class="c2h-identifier">restrict</span> <span class="c2h-identifier">src</span>, </td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-kword c2h-kword-void">void</span> <span class="c2h-operator">*</span><span class="c2h-identifier">restrict</span> <span class="c2h-identifier">dst</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p>
|
|
L'argomento <code>af</code> è la famiglia di indirizzi, ossia <code>AF_INET</code> nel nostro caso. Invece <code>src</code> è la stringa (zero-terminata) contenente l'indirizzo in rappresentazione dotted decimal. L'argomento <code>dst</code> è il riferimento ad una struttura <code>struct in_addr</code> dove vogliamo che la funzione scriva l'indirizzo generato. Se tutto va bene, allora la funzione ritorna 1, altrimenti 0 o -1.
|
|
</p>
|
|
|
|
<p class="nota">
|
|
La funzione <code>inet_pton</code> può operare anche su indirizzi IPv6. In tal caso l'argomento <code>af</code> è pari a <code>AF_INET6</code> e <code>dst</code> punterà ad una struttura di tipo <code>struct in_addr6</code>. È per questo motivo che <code>dst</code> ha tipo <code>void*</code>, perchè può essere sia di tipo <code>struct in_addr</code> che <code>struct in_addr6</code>.
|
|
<br />
|
|
Esistono molte funzioni che effettuano questo genere di conversione. Un altro esempio è <code>inet_aton</code>, che però converte solo indirizzi IPv4.
|
|
</p>
|
|
|
|
<p>
|
|
Dato che sia client che server saranno eseguiti sullo stesso computer, possiamo considerare l'indirizzo IP del server pari a <code>127.0.0.1</code>, quindi la connessione diventerà
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><string.h></span> <span class="c2h-comment">// memset</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><unistd.h></span> <span class="c2h-comment">// Questa definisce [close]</span></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><arpa/inet.h></span></td></tr>
|
|
<tr><td>5</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><netinet/in.h></span></td></tr>
|
|
<tr><td>6</td><td></td></tr>
|
|
<tr><td>7</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>8</td><td>{</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">socket</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">SOCK_STREAM</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">fd</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-comment">// Non sono riuscito a creare il socket TCP!</span></td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td></td></tr>
|
|
<tr><td>15</td><td> <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-identifier">ip</span>[] <span class="c2h-operator">=</span> <span class="c2h-val-str">"127.0.0.1"</span>;</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-kword c2h-kword-unsigned">unsigned</span> <span class="c2h-kword c2h-kword-short">short</span> <span class="c2h-identifier">port</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">8080</span>;</td></tr>
|
|
<tr><td>17</td><td></td></tr>
|
|
<tr><td>18</td><td> <span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span> <span class="c2h-identifier">addr</span>;</td></tr>
|
|
<tr><td>19</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_family</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">AF_INET</span>;</td></tr>
|
|
<tr><td>20</td><td> <span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_port</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">htons</span>(<span class="c2h-identifier">port</span>);</td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-identifier c2h-fcallname">memset</span>(<span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>, <span class="c2h-val-int">0</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_zero</span>));</td></tr>
|
|
<tr><td>22</td><td> </td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">inet_pton</span>(<span class="c2h-identifier">AF_INET</span>, <span class="c2h-identifier">ip</span>, <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>.<span class="c2h-identifier">sin_addr</span>) <span class="c2h-operator">!=</span> <span class="c2h-val-int">1</span>) {</td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-comment">// Non sono riuscito a convertire l'indirizzo IP!!</span></td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>26</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>27</td><td> }</td></tr>
|
|
<tr><td>28</td><td></td></tr>
|
|
<tr><td>29</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier c2h-fcallname">connect</span>(<span class="c2h-identifier">fd</span>, (<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr</span><span class="c2h-operator">*</span>) <span class="c2h-operator">&</span><span class="c2h-identifier">addr</span>, </td></tr>
|
|
<tr><td>30</td><td> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-kword c2h-kword-struct">struct</span> <span class="c2h-identifier">sockaddr_in</span>))) {</td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-comment">// La connessione è fallita!!</span></td></tr>
|
|
<tr><td>32</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>33</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>34</td><td> }</td></tr>
|
|
<tr><td>35</td><td></td></tr>
|
|
<tr><td>36</td><td> <span class="c2h-comment">// .. fai cose col socket ..</span></td></tr>
|
|
<tr><td>37</td><td></td></tr>
|
|
<tr><td>38</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>); <span class="c2h-comment">// Quando abbiamo finito, possiamo chiuderlo.</span></td></tr>
|
|
<tr><td>39</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>40</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
A questo punto anche la configurazione del client è conclusa!
|
|
</p>
|
|
|
|
<h2 id="la-business-logic">La business logic</h2>
|
|
<p>
|
|
Una volta connessi client e server, sarà il client a mandare il primo messaggo, quindi la prima cosa che farà il server dopo la connessione (cioè l'<code>accept</code>) è una chiamata a <code>recv</code> (dall'inglese, "ricevi"), mentre il client dopo la connessione (<code>connect</code>) chiamerà <code>send</code> (dall'inglese, "invia").
|
|
</p>
|
|
|
|
<h3 id="send">Send</h3>
|
|
<p>
|
|
La funzione send è definita così
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier c2h-fdeclname">send</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-void">void</span> <span class="c2h-operator">*</span><span class="c2h-identifier">buf</span>, <span class="c2h-identifier">size_t</span> <span class="c2h-identifier">len</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">flags</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
Dove <code>sockfd</code> è il socket relativo alla connessione (quello ritornato da <code>accept</code> nel server, oppure quello ritornato da <code>socket</code> nel client). Gli argomenti <code>buf</code> e <code>len</code> fanno riferimento all buffer che deve essere inviato e quanto è grande. Infine <code>flags</code> è un argomento che permette di configurare il modo in cui viene effettuato l'invio (per saperne di più sui flags, leggi <a href="aux_flag.html" target="_blank">questo</a>). Il valore di default di <code>flags</code> è <code>0</code>, ossia quel che useremo noi. Il valore di ritorno invece è -1 se qualcosa è andato storto, oppure il numero di byte inviati. È possibile che il valore di ritorno sia non negativo ma inferiore alla dimensione del buffer, quindi solo una parte del buffer è stata inviata, in tal caso dovremmo manualmente ritentare l'invio della parte mancante!
|
|
</p>
|
|
|
|
<p class="nota">
|
|
La funzione <code>send</code>, quando l'argomento <code>flags</code> è posto a <code>0</code>, è equivalente alla funzione <code>write</code>. La funzione <code>send</code> opera solo sui socket, mentre <code>write</code> opera sui file in generale (un socket è un tipo di file).
|
|
<br />
|
|
<br />
|
|
Il tipo <code>ssize_t</code> è una versione con segno di <code>size_t</code> (cioè può assumere valori negativi). Questi tipi di dato sono usati di solito per mantenere la dimensione di regioni di memoria.
|
|
</p>
|
|
|
|
<h3 id="recv">Recv</h3>
|
|
<p>
|
|
La funzione <code>recv</code> è abbastanza simmetrica rispetto alla sorella <code>send</code>. L'interfaccia è questa qui:
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><sys/socket.h></span></td></tr>
|
|
<tr><td>2</td><td></td></tr>
|
|
<tr><td>3</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier c2h-fdeclname">recv</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">sockfd</span>, <span class="c2h-kword c2h-kword-void">void</span> <span class="c2h-operator">*</span><span class="c2h-identifier">buf</span>, <span class="c2h-identifier">size_t</span> <span class="c2h-identifier">len</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">flags</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<p>
|
|
Anche qui <code>sockfd</code> è il socket della connessione, tuttavia il buffer <code>buff</code> con dimensione <code>len</code> non sarà letto ma scritto. Il valore di <code>len</code> è considerato più come il numero massimo di byte da leggere, infatti il valore di ritorno, che è il numero di byte ricevuti, può solo essere inferiore o uguale a <code>len</code>. Tuttavia, in caso di errore è ritornato <code>-1</code>. Per quanto riguada il parametro <code>flags</code>, il comportamento è analogo a <code>send</code>.
|
|
</p>
|
|
|
|
<p>
|
|
Una cosa aggiuntiva da notare è che quando <code>recv</code> ritorna <code>0</code> significa che il nodo col quale siamo in comunicazione si è disconnesso.
|
|
</p>
|
|
|
|
<p class="nota">
|
|
La relazione fra <code>send</code> e <code>write</code> si ha anche nel caso di <code>recv</code> e <code>read</code>.
|
|
</p>
|
|
|
|
<h3 id="la-business-logic-del-client">
|
|
La business logic del client
|
|
</h3>
|
|
|
|
<p>
|
|
Per quanto riguarda il client, un messaggio che verrà mandato al server per essere convertito. Quando la risposta sarà ricevuta, il programma la stampera a video. Avremo una cosa del genere (ho rimosso il codice che abbiamo scritto sino ad ora per poterci concetrare su quello che dobbiamo fare ora)
|
|
</p>
|
|
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-comment">/* ..gli header qui.. */</span></td></tr>
|
|
<tr><td>2</td><td><span class="c2h-directive">#include</span> <span class="c2h-val-str"><stdio.h></span></td></tr>
|
|
<tr><td>3</td><td></td></tr>
|
|
<tr><td>4</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier c2h-fdeclname">super_send</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span>, <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-operator">*</span><span class="c2h-identifier">buf</span>, <span class="c2h-identifier">size_t</span> <span class="c2h-identifier">len</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">flags</span>)</td></tr>
|
|
<tr><td>5</td><td>{</td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-kword c2h-kword-do">do</span> {</td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">send</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">buf</span> <span class="c2h-operator">+</span> <span class="c2h-identifier">sent_bytes</span>, </td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-identifier">len</span> <span class="c2h-operator">-</span> <span class="c2h-identifier">sent_bytes</span>, <span class="c2h-identifier">flags</span>);</td></tr>
|
|
<tr><td>10</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>12</td><td> <span class="c2h-kword c2h-kword-break">break</span>;</td></tr>
|
|
<tr><td>13</td><td> }</td></tr>
|
|
<tr><td>14</td><td> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">+=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>15</td><td> } <span class="c2h-kword c2h-kword-while">while</span>((<span class="c2h-identifier">size_t</span>) <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator"><</span> <span class="c2h-identifier">len</span>);</td></tr>
|
|
<tr><td>16</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-identifier">sent_bytes</span>;</td></tr>
|
|
<tr><td>17</td><td>}</td></tr>
|
|
<tr><td>18</td><td></td></tr>
|
|
<tr><td>19</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier c2h-fdeclname">super_recv</span>(<span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span>, <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-operator">*</span><span class="c2h-identifier">buf</span>, <span class="c2h-identifier">size_t</span> <span class="c2h-identifier">len</span>, <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">flags</span>)</td></tr>
|
|
<tr><td>20</td><td>{</td></tr>
|
|
<tr><td>21</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>22</td><td> <span class="c2h-kword c2h-kword-do">do</span> {</td></tr>
|
|
<tr><td>23</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">recv</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">buf</span> <span class="c2h-operator">+</span> <span class="c2h-identifier">received_bytes</span>, </td></tr>
|
|
<tr><td>24</td><td> <span class="c2h-identifier">len</span> <span class="c2h-operator">-</span> <span class="c2h-identifier">received_bytes</span>, <span class="c2h-identifier">flags</span>);</td></tr>
|
|
<tr><td>25</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><=</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>26</td><td> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>27</td><td> <span class="c2h-kword c2h-kword-break">break</span>;</td></tr>
|
|
<tr><td>28</td><td> }</td></tr>
|
|
<tr><td>29</td><td> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">+=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>30</td><td> } <span class="c2h-kword c2h-kword-while">while</span>((<span class="c2h-identifier">size_t</span>) <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator"><</span> <span class="c2h-identifier">len</span>);</td></tr>
|
|
<tr><td>31</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-identifier">received_bytes</span>;</td></tr>
|
|
<tr><td>32</td><td>}</td></tr>
|
|
<tr><td>33</td><td></td></tr>
|
|
<tr><td>34</td><td><span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier c2h-fdeclname">main</span>(<span class="c2h-kword c2h-kword-void">void</span>)</td></tr>
|
|
<tr><td>35</td><td>{</td></tr>
|
|
<tr><td>36</td><td> <span class="c2h-kword c2h-kword-int">int</span> <span class="c2h-identifier">fd</span> <span class="c2h-operator">=</span> <span class="c2h-comment">/* ..creazione del socket.. */</span></td></tr>
|
|
<tr><td>37</td><td> </td></tr>
|
|
<tr><td>38</td><td> <span class="c2h-comment">/* ..inizializzazione del socket.. */</span></td></tr>
|
|
<tr><td>39</td><td> </td></tr>
|
|
<tr><td>40</td><td> <span class="c2h-kword c2h-kword-const">const</span> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-operator">*</span><span class="c2h-identifier">message</span> <span class="c2h-operator">=</span> <span class="c2h-val-str">"Ossimoro"</span>;</td></tr>
|
|
<tr><td>41</td><td> </td></tr>
|
|
<tr><td>42</td><td> <span class="c2h-kword c2h-kword-char">char</span> <span class="c2h-identifier">risposta</span>[<span class="c2h-val-int">32</span>]; <span class="c2h-comment">// Il buffer che conterrà la risposta del server.</span></td></tr>
|
|
<tr><td>43</td><td> </td></tr>
|
|
<tr><td>44</td><td> <span class="c2h-comment">// Assumiamo che la lunghezza massima della risposta</span></td></tr>
|
|
<tr><td>45</td><td> <span class="c2h-comment">// sia 31 byte dato che deve entrare nel buffer </span></td></tr>
|
|
<tr><td>46</td><td> <span class="c2h-comment">// [risposta] (31 perchè dobbiamo aggiungere un byte</span></td></tr>
|
|
<tr><td>47</td><td> <span class="c2h-comment">// nullo alla fine per rendere la risposta una</span></td></tr>
|
|
<tr><td>48</td><td> <span class="c2h-comment">// stringa zero-terminata).</span></td></tr>
|
|
<tr><td>49</td><td></td></tr>
|
|
<tr><td>50</td><td> <span class="c2h-comment">// Dato che la risposta ha la stessa lunghezza del</span></td></tr>
|
|
<tr><td>51</td><td> <span class="c2h-comment">// nostro messaggio, possiamo assicurarci già ora</span></td></tr>
|
|
<tr><td>52</td><td> <span class="c2h-comment">// che la risposta non sforerà il buffer.</span></td></tr>
|
|
<tr><td>53</td><td> <span class="c2h-identifier">size_t</span> <span class="c2h-identifier">message_len</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">strlen</span>(<span class="c2h-identifier">message</span>);</td></tr>
|
|
<tr><td>54</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">message_len</span> <span class="c2h-operator">></span> <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">risposta</span>)<span class="c2h-operator">-</span><span class="c2h-val-int">1</span>) { <span class="c2h-comment">// NOTA: sizeof(risposta)-1 è 31</span></td></tr>
|
|
<tr><td>55</td><td> <span class="c2h-identifier c2h-fcallname">fprintf</span>(<span class="c2h-identifier">stderr</span>, <span class="c2h-val-str">"Il messaggio non può essere "</span></td></tr>
|
|
<tr><td>56</td><td> <span class="c2h-val-str">"più lungo di %ld caratteri\n"</span>, <span class="c2h-kword c2h-kword-sizeof">sizeof</span>(<span class="c2h-identifier">risposta</span>)<span class="c2h-operator">-</span><span class="c2h-val-int">1</span>);</td></tr>
|
|
<tr><td>57</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>58</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>59</td><td> }</td></tr>
|
|
<tr><td>60</td><td> </td></tr>
|
|
<tr><td>61</td><td> <span class="c2h-comment">// Invia il messaggio!</span></td></tr>
|
|
<tr><td>62</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">super_send</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">message</span>, <span class="c2h-identifier">message_len</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>63</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>64</td><td> <span class="c2h-comment">/* Qualcosa è andato storto! */</span></td></tr>
|
|
<tr><td>65</td><td> <span class="c2h-identifier c2h-fcallname">fprintf</span>(<span class="c2h-identifier">stderr</span>, <span class="c2h-val-str">"Non sono riuscito ad inviare [%s]\n"</span>, <span class="c2h-identifier">message</span>);</td></tr>
|
|
<tr><td>66</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>67</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>68</td><td> }</td></tr>
|
|
<tr><td>69</td><td> </td></tr>
|
|
<tr><td>70</td><td> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">super_recv</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">risposta</span>, <span class="c2h-identifier">message_len</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>71</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>72</td><td> <span class="c2h-comment">/* Qualcosa è andato storto! */</span></td></tr>
|
|
<tr><td>73</td><td> <span class="c2h-identifier c2h-fcallname">fprintf</span>(<span class="c2h-identifier">stderr</span>, <span class="c2h-val-str">"La ricezione della risposta a [%s] è fallita!"</span>, <span class="c2h-identifier">message</span>);</td></tr>
|
|
<tr><td>74</td><td> <span class="c2h-identifier c2h-fcallname">close</span>(<span class="c2h-identifier">fd</span>);</td></tr>
|
|
<tr><td>75</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>76</td><td> }</td></tr>
|
|
<tr><td>77</td><td> </td></tr>
|
|
<tr><td>78</td><td> <span class="c2h-comment">// A questo punto [recv] avrà scritto esattamente [n] byte</span></td></tr>
|
|
<tr><td>79</td><td> <span class="c2h-comment">// all'interno di [risposta], per cui il primo byte dopo il</span></td></tr>
|
|
<tr><td>80</td><td> <span class="c2h-comment">// messaggio è proprio alla posizione [risposta[n]]. Ora </span></td></tr>
|
|
<tr><td>81</td><td> <span class="c2h-comment">// possiamo aggiungere il byte nullo terminatore:</span></td></tr>
|
|
<tr><td>82</td><td> <span class="c2h-identifier">risposta</span>[<span class="c2h-identifier">n</span>] <span class="c2h-operator">=</span> <span class="c2h-val-char">'\0'</span>;</td></tr>
|
|
<tr><td>83</td><td> </td></tr>
|
|
<tr><td>84</td><td> <span class="c2h-comment">// E stampare il messaggio</span></td></tr>
|
|
<tr><td>85</td><td> <span class="c2h-identifier c2h-fcallname">printf</span>(<span class="c2h-val-str">"[%s] -> [%s]\n"</span>, <span class="c2h-identifier">message</span>, <span class="c2h-identifier">risposta</span>);</td></tr>
|
|
<tr><td>86</td><td> </td></tr>
|
|
<tr><td>87</td><td> <span class="c2h-comment">/* ..chiudi il socket.. */</span></td></tr>
|
|
<tr><td>88</td><td> <span class="c2h-kword c2h-kword-return">return</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>89</td><td>}</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
In realtà in questo codice abbiamo barato un po'. Non è necessariamente vero che <code>recv</code> e <code>send</code> inviino e ricevano l'intero messaggio con una sola chiamata. Per inviare il messaggio potrebbero servire due o più <code>send</code>! La necessità di chiamare iterativamente queste funzioni sorge quando il loro valore di ritorno (cioè <code>n</code> in questo esempio) è positivo ma comunque inferiore alla lunghezza del messaggio che abbiamo inviato. In questo programma non è un problema grave, perchè nel peggiore delle ipotesi risulterà che il messaggio del server è corretto a meno di qualche byte finale mancante.
|
|
</p>
|
|
|
|
<p>
|
|
Per rendere il nostro programma più robusto, la <code>send</code> potrebbe essere chiamata in questo modo qui
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>2</td><td><span class="c2h-kword c2h-kword-do">do</span> {</td></tr>
|
|
<tr><td>3</td><td> <span class="c2h-comment">// [message[i] + sent_bytes] è l'indirizzo della parte</span></td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-comment">// del messaggio non ancora inviato, mentre la sua</span></td></tr>
|
|
<tr><td>5</td><td> <span class="c2h-comment">// lunghezza è [message_len - sent_bytes].</span></td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">send</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">messages</span>[<span class="c2h-identifier">i</span>] <span class="c2h-operator">+</span> <span class="c2h-identifier">sent_bytes</span>, <span class="c2h-identifier">message_len</span> <span class="c2h-operator">-</span> <span class="c2h-identifier">sent_bytes</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>7</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-operator">-</span><span class="c2h-val-int">1</span>;</td></tr>
|
|
<tr><td>9</td><td> <span class="c2h-kword c2h-kword-break">break</span>;</td></tr>
|
|
<tr><td>10</td><td> }</td></tr>
|
|
<tr><td>11</td><td> <span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator">+=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>12</td><td>} <span class="c2h-kword c2h-kword-while">while</span>(<span class="c2h-identifier">sent_bytes</span> <span class="c2h-operator"><</span> <span class="c2h-identifier">message_len</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
In questo modo <code>send</code> verrà chiamata finchè l'interò messaggio sarà stato inviato, oppure un errore sarà avvenuto. Questo loop nel complesso si comporta come una <code>send</code> dove il valore di ritorno è <code>sent_bytes</code> ma che se invia il messaggio, non è mai parzialmente.
|
|
</p>
|
|
<p>
|
|
Quello che potremmo fare è creare una funzione ausiliaria <code>super_send</code> che effettua questo meccanismo per poi sostituirla alla semplice <code>send</code> interna al nostro programma.
|
|
</p>
|
|
<p>
|
|
Come accennato lo stesso problema si pone per <code>recv</code>. È possibile che ci vogliano più chiamate per ricevere l'intero messaggio. Per risolvere questo problema, come per la <code>send</code>, si potrebbe aggiungere un loop attorno alla sua chiamata
|
|
</p>
|
|
<div class="c2h-code">
|
|
<div class="c2h-code-inner">
|
|
<table>
|
|
<tr><td>1</td><td><span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-val-int">0</span>;</td></tr>
|
|
<tr><td>2</td><td><span class="c2h-kword c2h-kword-do">do</span> {</td></tr>
|
|
<tr><td>3</td><td> <span class="c2h-identifier">ssize_t</span> <span class="c2h-identifier">n</span> <span class="c2h-operator">=</span> <span class="c2h-identifier c2h-fcallname">recv</span>(<span class="c2h-identifier">fd</span>, <span class="c2h-identifier">risposta</span> <span class="c2h-operator">+</span> <span class="c2h-identifier">received_bytes</span>, <span class="c2h-identifier">message_len</span> <span class="c2h-operator">-</span> <span class="c2h-identifier">received_bytes</span>, <span class="c2h-val-int">0</span>);</td></tr>
|
|
<tr><td>4</td><td> <span class="c2h-kword c2h-kword-if">if</span>(<span class="c2h-identifier">n</span> <span class="c2h-operator"><=</span> <span class="c2h-val-int">0</span>) {</td></tr>
|
|
<tr><td>5</td><td> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>6</td><td> <span class="c2h-kword c2h-kword-break">break</span>;</td></tr>
|
|
<tr><td>7</td><td> }</td></tr>
|
|
<tr><td>8</td><td> <span class="c2h-identifier">received_bytes</span> <span class="c2h-operator">+=</span> <span class="c2h-identifier">n</span>;</td></tr>
|
|
<tr><td>9</td><td>} <span class="c2h-kword c2h-kword-while">while</span>(<span class="c2h-identifier">received_bytes</span> <span class="c2h-operator"><</span> <span class="c2h-identifier">message_len</span>);</td></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<p>
|
|
Le stesse considerazione rispetto alla receive valgono: questo loop si comporta come una versione augmentata di <code>recv</code> che però non riceve mai il messaggio parzialmente, a patto che non avvenga un errore (in tal caso <code>received_bytes == -1</code>) e che il server non si disconnetta (<code>received_bytes == 0</code>).
|
|
</p>
|
|
|
|
<p>
|
|
Il codice completo del client avendo aggiunto <code>super_send</code> e <code>super_recv</code> è
|
|
</p>
|
|
{% code %}
|
|
#include <string.h> // memset
|
|
#include <unistd.h> // Questa definisce [close]
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
ssize_t super_send(int fd, const char *buf, size_t len, int flags)
|
|
{
|
|
ssize_t sent_bytes = 0;
|
|
do {
|
|
ssize_t n = send(fd, buf + sent_bytes,
|
|
len - sent_bytes, flags);
|
|
if(n < 0) {
|
|
sent_bytes = -1;
|
|
break;
|
|
}
|
|
sent_bytes += n;
|
|
} while((size_t) sent_bytes < len);
|
|
return sent_bytes;
|
|
}
|
|
ssize_t super_recv(int fd, char *buf, size_t len, int flags)
|
|
{
|
|
ssize_t received_bytes = 0;
|
|
do {
|
|
ssize_t n = recv(fd, buf + received_bytes,
|
|
len - received_bytes, flags);
|
|
if(n <= 0) {
|
|
received_bytes = n;
|
|
break;
|
|
}
|
|
received_bytes += n;
|
|
} while((size_t) received_bytes < len);
|
|
return received_bytes;
|
|
}
|
|
int main(void)
|
|
{
|
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(fd < 0) {
|
|
// Non sono riuscito a creare il socket TCP!
|
|
return -1;
|
|
}
|
|
const char ip[] = "127.0.0.1";
|
|
unsigned short port = 8080;
|
|
struct sockaddr_in addr;
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
|
|
|
|
if(inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
|
|
// Non sono riuscito a convertire l'indirizzo IP!!
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if(connect(fd, (struct sockaddr*) &addr,
|
|
sizeof(struct sockaddr_in))) {
|
|
// La connessione è fallita!!
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
const char *message = "Ossimoro";
|
|
char risposta[32]; // Il buffer che conterrà la risposta del server.
|
|
size_t message_len = strlen(message);
|
|
if(message_len > sizeof(risposta)-1) { // NOTA: sizeof(risposta)-1 è 31
|
|
fprintf(stderr, "Il messaggio non può essere "
|
|
"più lungo di %ld caratteri\n", sizeof(risposta)-1);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
// Invia il messaggio!
|
|
ssize_t n = super_send(fd, message, message_len, 0);
|
|
if(n < 0) {
|
|
/* Qualcosa è andato storto! */
|
|
fprintf(stderr, "Non sono riuscito ad inviare [%s]\n", message);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
n = super_recv(fd, risposta, message_len, 0);
|
|
if(n < 0) {
|
|
/* Qualcosa è andato storto! */
|
|
fprintf(stderr, "La ricezione della risposta a [%s] è fallita!", message);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
risposta[n] = '\0';
|
|
printf("[%s] -> [%s]\n", message, risposta);
|
|
|
|
close(fd); // Quando abbiamo finito, possiamo chiuderlo.
|
|
return 0;
|
|
}
|
|
{% end %}
|
|
<p>
|
|
Decisamente un bel pezzo di programma!
|
|
</p>
|
|
|
|
<h2 id="la-business-logic-del-server">
|
|
La business logic del server
|
|
</h2>
|
|
<p>
|
|
Il server dalla sua parte, accetterà una connessione, riceverà il messaggio e dopo averlo convertito lo rimanderà al client. Una volta inviato tornera ad attendere per una nuova connessione.
|
|
</p>
|
|
{% code %}
|
|
/* ..qui gli header.. */
|
|
#include <ctype.h> // isupper, tolower, toupper
|
|
ssize_t super_send(int fd, const char *buf, size_t len, int flags)
|
|
{
|
|
ssize_t sent_bytes = 0;
|
|
do {
|
|
ssize_t n = send(fd, buf + sent_bytes,
|
|
len - sent_bytes, flags);
|
|
if(n < 0) {
|
|
sent_bytes = -1;
|
|
break;
|
|
}
|
|
sent_bytes += n;
|
|
} while((size_t) sent_bytes < len);
|
|
return sent_bytes;
|
|
}
|
|
int main(void)
|
|
{
|
|
int fd = /* .. crea il socket.. */
|
|
/* ..inizializzalo.. */
|
|
|
|
while(1) {
|
|
int clifd = accept(fd, NULL, NULL);
|
|
if(clifd < 0)
|
|
/* Errore durante l'accettazione */
|
|
continue; // Torna ad accettare richieste
|
|
|
|
char buffer[32];
|
|
|
|
// In questo caso usiamo [recv] e non [super_recv]
|
|
// perchè non conosciamo la dimensione del messaggio
|
|
// a priori.
|
|
ssize_t n = recv(clifd, buffer, sizeof(buffer)-1, 0);
|
|
if(n <= 0) {
|
|
// C'è stato un errore oppure
|
|
// il client si è disconnesso.
|
|
// Chiudi il server e torna ad
|
|
// aspettare per nuove richieste.
|
|
close(clifd);
|
|
continue;
|
|
}
|
|
buffer[n] = '\0';
|
|
/* Converti la stringa */
|
|
for(int i = 0; buffer[i] != '\0'; i += 1) {
|
|
char c = buffer[i];
|
|
if(isupper(c))
|
|
c = tolower(c);
|
|
else
|
|
c = toupper(c);
|
|
buffer[i] = c;
|
|
}
|
|
|
|
// Ed ora inviala dinuovo
|
|
super_send(clifd, buffer, n, 0);
|
|
|
|
// Non controlliamo il valore di ritorno di
|
|
// [super_send] perchè in ogni caso chiuderemmo
|
|
// il socket.
|
|
|
|
close(clifd);
|
|
}
|
|
|
|
/* ..chiudi il socket.. */
|
|
return 0;
|
|
}
|
|
{% end %}
|
|
<p>
|
|
Aggiungendo la porzione di inizializzazione e le altre omesse, si ottiene
|
|
</p>
|
|
{% code %}
|
|
#include <string.h> // memset
|
|
#include <unistd.h> // Questa definisce [close]
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <ctype.h> // isupper, tolower, toupper
|
|
ssize_t super_send(int fd, const char *buf, size_t len, int flags)
|
|
{
|
|
ssize_t sent_bytes = 0;
|
|
do {
|
|
ssize_t n = send(fd, buf + sent_bytes,
|
|
len - sent_bytes, flags);
|
|
if(n < 0) {
|
|
sent_bytes = -1;
|
|
break;
|
|
}
|
|
sent_bytes += n;
|
|
} while((size_t) sent_bytes < len);
|
|
return sent_bytes;
|
|
}
|
|
int main(void)
|
|
{
|
|
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(fd < 0) {
|
|
// Non sono riuscito a creare il socket TCP!
|
|
return -1;
|
|
}
|
|
|
|
struct sockaddr_in addr;
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(8080);
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
|
|
|
|
if(bind(fd, (struct sockaddr*) &addr,
|
|
sizeof(struct sockaddr_in))) {
|
|
// Il binding è fallito!!
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if(listen(fd, 32)) {
|
|
// Non sono riuscito ad ascoltare!
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
while(1) {
|
|
int clifd = accept(fd, NULL, NULL);
|
|
if(clifd < 0)
|
|
/* Errore durante l'accettazione */
|
|
continue; // Torna ad accettare richieste
|
|
|
|
char buffer[32];
|
|
ssize_t n = recv(clifd, buffer, sizeof(buffer)-1, 0);
|
|
if(n <= 0) {
|
|
close(clifd);
|
|
continue;
|
|
}
|
|
buffer[n] = '\0';
|
|
/* Converti la stringa */
|
|
for(int i = 0; buffer[i] != '\0'; i += 1) {
|
|
char c = buffer[i];
|
|
if(isupper(c))
|
|
c = tolower(c);
|
|
else
|
|
c = toupper(c);
|
|
buffer[i] = c;
|
|
}
|
|
super_send(clifd, buffer, n, 0);
|
|
close(clifd);
|
|
}
|
|
close(fd); // Quando abbiamo finito, possiamo chiuderlo.
|
|
return 0;
|
|
}
|
|
{% end %}
|
|
<p>
|
|
Per come è stato scritto questo programma, il controllo non uscirà mai dal <code>while</code>, per cui l'ultima chiamata a <code>close</code>, in realtà, non sarà mai eseguita! Il motivo per cui l'ho lasciata è per mostrare comunque la struttura generale del programma che usa i socket.
|
|
</p>
|
|
|
|
<p class="nota">
|
|
In realtà i nomi <code>super_send</code> e <code>super_recv</code> sono intesi come nomi umoristici, nomi più appropriati sarebbero <code>send_all</code> e <code>recv_all</code>.
|
|
</p>
|
|
|
|
<h2 id="riferimenti">Riferimenti</h2>
|
|
<ul>
|
|
{% for riferimento in riferimenti %}
|
|
<li>
|
|
<a href="{{riferimento.link}}">{{riferimento.link}}</a>
|
|
</li>
|
|
</ul>
|
|
</article>
|
|
</main>
|
|
</body>
|
|
</html> |