Die Programmiersprache Ruby (German Edition)
von
recvfrom
legt die maximale Datenmenge fest, die wir empfangen wollen. In diesem Fall begrenzen wir unseren Client und Server auf eine maximale Übertragung von einem Kilobyte:
require 'socket' # Standardbibliothek
host, port, request = ARGV # Argumente von der Befehlszeile holen
ds = UDPSocket.new # Datagramm-Socket erzeugen
ds.connect(host, port) # Mit dem Port auf dem Host verbinden
ds.send(request, 0) # Text schicken
response,address = ds.recvfrom(1024) # Auf Antwort warten (maximal 1 KB)
puts response # Antwort ausgeben
Der Servercode nutzt die Klasse
UDPSocket
genauso wie der Clientcode – es gibt keine spezielle
UDPServer
-Klasse für datagrammbasierte Server. Statt
connect
für die Verbindung des Sockets aufzurufen, nutzt unserer Server
bind
, um dem Socket mitzuteilen, an welchem Port er zu lauschen hat. Der Server verwendet dann
send
und
recvfrom
so wie der Client, aber in umgekehrter Reihenfolge. Er ruft
recvfrom
auf, um zu warten, bis er ein Datagramm am angegebenen Port erhält. Wenn das geschieht, wandelt der Server den empfangenen Text in Großbuchstaben um und schickt ihn zurück. Zu beachten ist, dass die Methode
recvfrom
zwei Werte zurückgibt. Der erste enthält die empfangenen Daten. Der zweite ist ein Array, das Informationen darüber enthält, woher die Daten gekommen sind. Wir extrahieren die Host- und Portinformationen aus diesem Array und verwenden sie, um die Antwort an den Client zurückzuschicken:
require 'socket' # Standardbibliothek
port = ARGV[0] # Port, an dem gelauscht werden soll
ds = UDPSocket.new # Neuen Socket erzeugen
ds.bind(nil, port) # Am Port lauschen lassen
loop do # Unendliche Schleife
request,address=ds.recvfrom(1024) # Darauf warten, dass etwas kommt
response = request.upcase # Text in Großbuchstaben umwandeln
clientaddr = address[3] # Welche IP-Adresse hat die Anfrage geschickt?
clientname = address[2] # Wie ist der Hostname?
clientport = address[1] # Von welchem Port kam sie?
ds.send(response, 0, # Schicke die Antwort dorthin,
clientaddr, clientport) # wo die Anfrage herkam
# Verbindung mit dem Client protokollieren
puts "Verbindung von: #{clientname} #{clientaddr} #{clientport}"
end
9.8.4 Ein komplexerer Client
Der folgende Code ist ein umfassender entwickelter Internetclient im Stil von telnet . Er verbindet sich mit dem angegebenen Host und Port und läuft dann in einer Schleife, in der er eine Eingabezeile von der Konsole liest, sie an den Server schickt und dann die Antwort des Servers einliest und ausgibt. Der Client zeigt, wie man die lokalen und entfernten Adressen der Netzwerkverbindung ermittelt, enthält Exception-Handling und verwendet die
IO
-Methoden
read_nonblock
und
readpartial
, die weiter oben in diesem Kapitel beschrieben wurden. Der Code ist umfassend dokumentiert und sollte selbsterklärend sein:
require 'socket' # Sockets aus der Standardbibliothek
host, port = ARGV # Netzwerkhost und -port von der Befehlszeile
begin # Begin für Exception-Handling
# Feedback an den Benutzer, während die Verbindung aufgebaut wird
STDOUT.print "Connecting..." # Erzählen, was wir tun
STDOUT.flush # Direkt ausgeben
s = TCPSocket.open(host, port) # Verbinden
STDOUT.puts "done" # Und sagen, dass wir es getan haben
# Jetzt Informationen über die Verbindung anzeigen
local, peer = s.addr, s.peeraddr
STDOUT.print "Verbunden mit #{peer[2]}:#{peer[1]}"
STDOUT.puts " über den lokalen Port #{local[1]}"
# Ein bisschen warten, um zu sehen, ob der Server eine initiale Nachricht schickt
begin
sleep(0.5) # Eine halbe Sekunde warten
msg = s.read_nonblock(4096) # Alles lesen, was schon da ist
STDOUT.puts msg.chop # Und anzeigen
rescue SystemCallError
# Wenn nichts zu lesen da war, die Exception einfach ignorieren
end
# Nun eine Schleife für die Client/Server-Interaktion beginnen
loop do
STDOUT.print '> ' # Prompt für die lokale Eingabe anzeigen
STDOUT.flush # Sicherstellen, dass die Ausgabe sichtbar ist
local = STDIN.gets # Zeile von der Konsole lesen
break if !local # Beenden, wenn keine Eingabe von der Konsole kommt
s.puts(local) # Die Zeile an den Server schicken
s.flush # Ausgabe erzwingen
# Antwort des Servers lesen und ausgeben.
# Der Server kann mehr als eine Zeile schicken; daher readpartial nutzen,
# um alles zu lesen, was er schickt (solange alles in einem Stück kommt)
response = s.readpartial(4096) # Antwort des Servers lesen
puts(response.chop) # Antwort ausgeben
end
rescue # Wenn etwas schiefgeht,
puts $! #
Weitere Kostenlose Bücher