Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 5 di 32
  • livello principiante
Indice lezioni

Il networking a basso livello

La struttura di base della libreria socket e esempi client-server UDP e TCP
La struttura di base della libreria socket e esempi client-server UDP e TCP
Link copiato negli appunti

In Ruby il networking a basso livello è gestito dalla libreria standard socket che è strutturata nel seguente modo: alla base c'è BasicSocket classe astratta, sottoclasse di IO, che contiene alcuni metodi fondamentali, ereditati da tutte le sue sottoclassi, come ad esempio close_read, close_write, getpeername, getsockname, recv e send.

Da BasicSocket discendono direttamente IPSocket, Socket e UNIXSocket. IPSocket è la classe che implementa le socket che usano il protocollo di trasporto IP e ha due sottoclassi: TCPSocket e UDPSocket che rispettivamente trattano connessioni da, e verso, socket TCP e UDP. La classe Socket fornisce invece direttamente accesso all'implementazione delle socket del sistema operativo. UnixSocket infine gestisce le comunicazioni IPC utilizzando lo UNIX domain protocol.

Tutte le classi di socket discendono indirettamente dalla classe IO, questo vuol dire che è possibile usare i metodi di IO sui socket così come accade ad esempio per i file.

Invece di dare uno sguardo in dettaglio ai metodi di queste classi vediamo degli utili e semplici esempi che ne illustrano brevemente l'utilizzo. Per ragioni di spazio e opportunità non tratteremo le BSD socket API dandone per scontato l'utilizzo elementare. E per una panoramica delle suddette classi si rimanda alla documentazione ufficiale della libreria standard.

UDP server

Iniziamo con il classico esempio client-server, in questo caso il server invierà al client una frase generata dal programma "fortune", tipo messaggino del dolcetto della fortuna al ristorante cinese. Ecco il server:

require "socket"

server = UDPSocket.open
server.bind(nil, 12345)

loop do
  data, sender = server.recvfrom(1)
  chost = sender[3]
  cport = sender[1]

  fortunecookie = 'fortune'
  puts "Request from #{chost}:#{cport}"

  server.send(fortunecookie, 0, chost, cport)
end

Innanzitutto creiamo una nuova istanza della classe UDPSocket, in questo caso open è sinonimo di new e poi con bind leghiamo la connessione UDP ad un hostname e ad una porta.

Il ciclo principale del server non fa altro che mettersi in attesa di una connessione e lo fa con recvfrom che prende come argomento il numero di byte da leggere dal socket e restituisce un array contenete i dati ricevuti e le informazioni sul client.

Proprio da queste informazioni ricaviamo l'host e la porta del client che utilizziamo con send per rispondere al client; send prende come argomenti i dati da inviare, alcune opzioni, l'hostname e la porta del destinatario. Questo è invece il client:

require "socket"

client = UDPSocket.open

client.connect('localhost', 12345)
client.send("", 0)

while client.gets
  puts $_
end

In questo caso dopo aver creato un oggetto di tipo UDPSocket creiamo una connessione verso localhost sulla porta 12345 e inviamo una richiesta con send utilizzando la versione a due parametri e passando una stringa vuota dato che il nostro server non si cura di questo parametro. Infine ci mettiamo in ascolto della risposta del server che stampiamo a video. Anche in questo caso avremmo potuto passare a send anche l'hostname e la porta e quindi avremmo dovuto sostituire le righe

  client.connect('localhost', 12345)
  client.send("", 0)

con la riga

  client.send("", 0, 'localhost', 12345)

Ecco ora una breve sessione delle nostre mini-applicazioni appena scritte:

$ ruby UDPserver.rb
Request from 127.0.0.1:34317

e allo stesso momento il client otterrà una risposta:

$ ruby UDPclient.rb
I still maintain the point that designing a monolithic kernel in 1991 is a
fundamental error.  
Be thankful you are not my student.  You would not get a high grade for such a design :-)
(Andrew Tanenbaum to Linus Torvalds)

TCP server

Analogamente un server e un client TCP vanno scritti nel seguente modo:

require "socket"

server = TCPServer.open('localhost', 12345)

while session = server.accept
  fortunecookie = `fortune -e linuxcookiè  
  session.puts fortunecookie
  session.close
end

In questo caso abbiamo utilizzato la sottoclasse TCPServer di TCPSocket, ne abbiamo creato un istanza con open passando la porta e l'hostname come argomenti. Dopodiché, nel ciclo principale, con accept ci siamo messi in attesa di una connessione identificata da session che è di tipo TCPSocket. Infine mandiamo la frase generata da fortune e chiudiamo la connessione.

Il client conterrà del codice di questo tipo:

require "socket"

client = TCPSocket.open('localhost', 12345)

while client.gets
    puts $_
end

client.close

Il client è molto semplice, non facciamo altro che aprire una connessione TCP verso localhost sulla porta 12345 e poi stampiamo la risposta inviata dal server. Anche in questo caso una breve sessione delle nostre applicazioni:

$ ruby TCPserver.rb
$ ruby TCPclient.rb
Dijkstra probably hates me
(Linus Torvalds, in kernel/sched.c)

Le differenze principali tra le due coppie client-server sono dovute essenzialmente alla diversa natura dei protocolli TCP e UDP. Lascio come utile esercizio al lettore l'implementazione di un server TCP con la gestione delle richieste attraverso i thread.

Ti consigliamo anche