Programmazione client/server

Uso dei socket

Per scrivere due programmi che comunichino tra di loro inviandosi dei dati, (quindi al livello Applicazione dello standard ISO/OSI) è necessario poter effettuare delle chiamate al livello sottostante.
Nel modello ISO/OSI il livello sottostante di riferimento è quello di Presentazione, implementato nelle tecnologie RPC (Remote Procedure Call) e CORBA(Common Objetc Request Broker Architecture).
Nell'architettura TCP/IP il livello sottostante è invece quello di Trasporto, costituito essenzialmente dai protocolli TCP (Transfer Control Protocol) o UDP (User Datagram Protocol).
La chiamata a questi protocolli viene effettuata tramite la creazione di socket, che sono in pratica canali di comunicazione per l'invio e la ricezione di dati.

Lato server

Per quanto riguarda la programmazione del server, cioè del programma che rimane in ascolto e risponde alle richieste del client, sono previste le seguenti fasi:

Lato client

Per quanto riguarda il client, le operazioni da compiere sono:

Sincronizzazione

Blocked socket e timeout

Poichè il server e il client non sono sincronizzati nell'invio e nella ricezione dei dati - ognuno, ovviamente, procede per proprio conto - quando si effettua una lettura di dati da un socket, bisogna tener conto che non sono immediatamente disponibili.
Il comportamento di default di un socket è quindi quello di rimanere bloccato in attesa dell'arrivo dei dati dallo stream di ingresso(come quando un programma aspetta un input da tastiera).
In questo modo però se l'altra applicazione non invia niente, il blocco diventa indefinito.

Una possibile soluzione è l'uso del timeout. Il timeout è un tempo massimo di attesa, che è possibile impostare dopo aver creato il socket; trascorso questo tempo, viene generato un segnale che va poi gestito dal programma provvedendo a chiudere la connessione senza generare errori.
Questa tecnica è particolarmente importante nella programmazione server-side, per evitare che il server rimanga impegnato all'infinito per l'errata connessione di un client.

Multithreading

Nella trattazione precedente, si suppone che il server sia in grado di accettare una connessione per volta, mantenendo in una lista di attesa un certo numero di client quando è impegnato.
In realtà una tecnica molto migliore è quella di creare un thread per ogni connessione accettata, in modo che queste possono essere gestire in parallelo.
Per fare ciò è necessario ricorrere alla programmazione concorrente
Per il momento questo argomento non viene sviluppato ulteriormente

Istruzioni PHP per l'uso dei socket

CREAZIONE

$sock=socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
AF_INET = uso dell'architettura TCP/IP
SOCK_STREAM=typo di socket da usare con il protocollo TCP
SOL_TCP=protocollo TCP
Restituisce una risorsa ($sock) da usare nelle successive istruzioni
Vale sia per il server che per il client

COLLEGAMENTO

socket_bind($sock, indirizzoIP, porta);
Collega il socket a un indirizzo IP locale e a una porta.
Restituisce true o false
Si usa nel programma server

socket_connect($sock, indirizzoIP, porta);
Collega il socket a un indirizzo IP remoto e a una porta.
Restituisce true o false
Si usa nel client

ASCOLTO

socket_listen($sock,X);
X = numero massimo di connessioni accettate. Se si vuole usare il massimo,c'è la costante predefinita SOMAXCONN
Pone il socket ($sock) all'ascolto.
Restituisce true o false
Chiaramente, l'istruzione riguarda il server.

RICEZIONE

$s=socket_accept($sock);
Accetta la connessione da un client e crea un nuovo socket per la ricezione e l'invio dei dati.
Restituisce una risorsa ($s) da usare nelle successive istruzioni

LETTURA

$buffer=socket_read($s, X);
X=dimensione massima del buffer (di solito 1024 o un suo multiplo)
Legge i dati dal socket ($s) e li mette in $buffer.
La stringa inviata deve avere un carattere terminatore (\n, \0, \r), altrimenti si attendono X caratteri.
Vale sia per il server che per il client.
N.B.: per inibire i messaggi di errore, anteporre il carattere @
$buffer=@socket_read($s, X);

SCRITTURA

$N=socket_write($s, $buffer);
Scrive sul socket ($s) la stringa $buffer.
Restituisce il numero di caratteri spediti, oppure -1 (false) in caso di errore.
Vale sia per il server che per il client.

TIME_OUT

socket_set_option($s,SOL_SOCKET,SO_RCVTIMEO,$time);
Imposta il time-out sul socket $s in ricezione.
In alternativa, indicare SO_SNDTIMEO per la spedizione
Restituisce vero o falso.
$time deve essere una array associativo, impostato in questo modo:
$time["sec"]=secondi; $time[]=microsecondi;
dove secondi e microsecondi contengono il tempo di attesa desiderato
Vale sia per il server che per il client.