Possiamo sviluppare applicazioni di rete basate su socket utilizzando il linguaggio
Ruby. È necessario avere una conoscenza di base dei socket stile
BSD (Berkley Software Distribution). Attualmente, possiamo tranquillamente parlare genericamente di socket, mentre in passato sarebbe stato corretto parlare di socket BSD poiché erano presenti molte API per socket in competizione tra loro. Infine, l'API per socket
BSD ha avuto la meglio nel mondo UNIX/POSIX. La maggior parte degli sviluppatori []*nix[/], che ha a che fare con networking, usa socket in stile
BSD. Avendo pratica nella programmazione di connessioni di rete in C o C++, è sorprendentemente facile programmare le stesse cose in
Ruby.
Parliamo un po' delle basi del networking. Le nostre osservazioni sul networking si focalizzano sulla relazione client-server. Il client richiede che venga eseguita una certa operazione, e il server realizza l'azione e invia una risposta al client. Un’implementazione comune del modello richiesta-risposta è l'interazione tra i browser e i server Web. Quando un utente seleziona un sito Web attraverso un browser (l'applicazione client), viene inviata una richiesta al server Web appropriato (l'applicazione server); il server normalmente risponde al client inviando la pagina HTML appropriata.
Una porta non è un dispositivo fisico, ma un’astrazione per facilitare la comunicazione tra un server e il client. Le porte sono descritte da un valore intero a 16 bit. Quindi, una macchina può avere fino ad un massimo di 65535 numeri di porte (da 0 a 65535). I numeri di porta sono suddivisi in tre intervalli: le
Well Known Port, le
Registered Port e le
Dynamic o Private Port. Le
Well Known Port sono quelle da 0 a 1023 (per esempio: la porta 80 è per HTTP, la 25 per SMTP e così via). Le
Registered Port sono dal 1024 a 49151. Le
Dynamic o Private Port sono quelle da 49152 a 65535.
Gli indirizzi Internet sono gli indirizzi numerici di un host e sono formati da 4 byte come 132.162.4.102. L'indirizzo 127.0.0.1 (localhost) è un indirizzo speciale, detto indirizzo di
loopback, e denota la macchina locale.
Un socket rappresenta una singola connessione tra due applicazioni di rete. Queste due applicazioni nominalmente sono eseguite su due computer differenti, ma i socket possono essere utilizzati anche per la comunicazione interprocesso di un singolo computer. Le applicazioni possono creare socket per comunicare tra loro. I socket sono bidirezionali, intendendo con questo che entrambi gli estremi della connessione possono sia inviare che ricevere dati.
Ruby dispone di un insieme ricco e vario di classi per i socket, che vanno dalla classe standard
Socket (che riproduce l’API dei socket
BSD) fino a classi più raffinate, che concentrano l'attenzione su un particolare protocollo o tipo di applicazione. La gerarchia delle classi base per i socket è mostrata nella figura seguente, tratta dal testo “Programming Ruby”:
<center>

</center>
La classe
TCPSocket offre il supporto per i socket per il protocollo TCP. Una classe di aiuto per la creazione di socket server TCP è disponibile nella classe
TCPServer. Per creare un flusso socket e connetterlo ad un server, è possibile usare la classe
TCPSocket.
Ora costruiremo un server e client DataOra, basato su
Ruby, che mostra sul computer client la data e l'ora della locazione del server, utilizzando l’API socket di
Ruby. Ecco il codice per il server Data-Ora
p068dtserver.rb:
<span style="font-size:1.0em">
# Date Time Server - server side usando i thread
# usage: ruby p068dtserver.rb
require "socket"
dts = TCPServer.new('localhost', 20000)loop do
Thread.start(dts.accept) do |s|
print(s, " is acceptedn")
s.write(Time.now)
print(s, " is gonen")
s.close
end
end</span>
Prima di tutto viene caricata la libreria socket con il comando
require. La classe
TCPServer è la classe
helper per la costruzione dei server socket TCP. L'istruzione
<span style="font-size:1.0em">TCPServer.new('localhost', 20000)</span>
crea un nuovo socket identificato da localhost e numero. Il
<span style="font-size:1.0em">Thread.start</span>
crea ed esegue un nuovo thread per lanciare le istruzioni del blocco. Qualsiasi argomento passato al
<span style="font-size:1.0em">Thread.start</span>
viene passato al blocco. Il metodo
<span style="font-size:1.0em">dts.accept</span>
attende una connessione su
dts, e ritorna un nuovo oggetto
TCPSocket connesso con il chiamante. L'iteratore
<span style="font-size:1.0em">Kernel.loop</span>
chiama il blocco associato
do - end per sempre (o almento fino che non si esce dal
loop).
Usiamo
<span style="font-size:1.0em">s.write(Time.now)</span>
per scrivere la data corrente e l'ora del server sul socket. Infine, scriviamo
<span style="font-size:1.0em">s.close</span>
per chiudere un socket utilizzando il metodo
close. Quest’ultimo è ereditato dalla classe
IO, ma è disponibile per ciascun tipo di socket.
Visto che stiamo testando questi programmi sulla nostra macchina locale, apriamo una finestra di prompt, andiamo nella cartella che contiene il programma
p068dtserver.rb e digitiamo
ruby p068dtserver.rb. Il programma va in esecuzione e attende un client che si connetta sulla porta 20000.
Di seguito il codice per il client DataOra:
<span style="font-size:1.0em">
# Date Time Client
# usage: ruby p069dtclient.rb
require 'socket'
sock = TCPSocket.new('127.0.0.1', 20000)str = sock.recv(100)
print str
sock.close</span>
Per prima cosa viene caricata la libreria socket con il comando
require. L'istruzione
<span style="font-size:1.0em">sock = TCPSocket.new('127.0.0.1', 20000)</span>
apre una connessione TCP verso localhost alla porta 2000. L'istruzione
<span style="font-size:1.0em">str = sock.recv(100)</span>
permette di ricevere fino a 100 byte dal sock. Mostriamo la stringa data-ora ricevuta dal server e alla fine chiudiamo il socket.
"Traduzione e adattamento a cura di Francesca Beatrice Cice. La versione originale del tutorial di
Satish Talim può essere trovata su
rubylearning.com"