Ruby bietet zwei Möglichkeiten, über Sockets Daten auszutauschen.
Sehr bequem ist dabei, dass Socket eine Unterklasse der IO-Klasse
ist und damit die "`üblichen"' Ein- und Ausgabefunktionen anbietet, die
vom Umgang mit Dateien (ebenfalls IO-Unterklassen) bekannt sind.
Gelegentlich kann es aber effizienter sein, mit den Socket-typischen
Methoden send und recv zu arbeiten.
Die Methode accept wartet auf eine ankommende Anfrage
auf dem eingestellten Port und gibt eine Socket-Instanz zurück,
über die der Server mit dem Client kommunizieren kann.
require 'socket'
class IPSocket
def portInfo(out = STDOUT)
out.puts "local: #{addr[1]}\n" +
"remote: #{peeraddr[1]}"
end
end
serverSocket = TCPServer.new('localhost', 9876)
server = Thread.start {
while (clientSocket = serverSocket.accept)
clientSocket.portInfo
puts "Anfrage: #{clientSocket.gets}"
clientSocket.puts "Sie rufen außerhalb der " +
"Geschäftszeiten an ..."
end
}
clientSocket = TCPSocket.new('localhost', 9876)
clientSocket.portInfo
clientSocket.puts "Hallo?"
puts "Antwort: #{clientSocket.gets}"
Eine weitere Anwendungsmöglichkeit ist ein Portscanner, der die möglichen Ports auf empfangsbereite Server überprüft.
require 'socket'
1.upto(1024) do |port|
begin
t = TCPSocket.new('localhost', port)
rescue
# gesperrte Ports erzeugen einen Fehler,
# den wir ignorieren
else
t.close
s = Socket.getnameinfo(["AF_INET",
port, 'localhost'])
print "Port #{port} (#{s[1]})" +
" ist offen.\n"
end
port += 1
end
Das genaue Ergebnis ist selbstverständlich von der Konfiguration des verwendeten Computers abhängig und kann zum Beispiel so aussehen:
Port 21 (ftp) ist offen. Port 22 (ssh) ist offen. Port 23 (telnet) ist offen. Port 25 (smtp) ist offen. Port 37 (time) ist offen. Port 79 (finger) ist offen. Port 80 (http) ist offen. Port 111 (sunrpc) ist offen. Port 113 (ident) ist offen. Port 513 (login) ist offen. Port 515 (printer) ist offen.
Die bekannten Protokolle wie FTP, HTTP, SMTP und POP3 setzen alle
auf dem verbindungsorientierten Transmission
Control Protocol (TCP) auf.
Dabei wird stets eine permanente Verbindung zwischen Client und Server
aufgebaut. Die Verbindungsaufnahme geschieht bereits in der new-Methode
der TCPSocket-Klasse.
Für etwas schnellere Transferraten steht das "`verbindungslose"'
User Datagram Protocol UDP
zur Verfügung. Der Geschwindigkeitsvorteil kommt daher, dass UDP
die Daten "`auf gut Glück"' zum Empfänger
schickt und sich nicht darum kümmert, ob die Pakete in
der richtigen Reihenfolge eintreffen oder ob sie überhaupt
ankommen.
Damit muss wesentlich weniger Bandbreite für
Verwaltungsaufwand bereitgestellt werden, so dass höhere
Datenraten - bei potenziell höheren Fehlerraten - möglich
werden. Allerdings sollte man die maximale Größe
eines einzelnen Pakets klar festlegen, da ein UDPSocket
im Gegensatz zu Sockets verbindungsbasierter Protokolle nicht
mehrfach abgefragt werden kann.
require 'socket'
$port = 9876
$maxSize = 100
server = Thread.start {
serverSocket = UDPSocket.new
serverSocket.bind("localhost", 9876)
loop do
data = serverSocket.recvfrom($maxSize)
p data
end
}
def adhoc(msg, host="localhost", port=9876)
UDPSocket.new.send(msg, 0, host, port)
end
adhoc("Hallo, wie geht's dir?")
#-> ["Hallo, wie ...", ["AF_INET", 32790, ...
adhoc("Mir geht's gut.")
#-> ["Mir geht's ...", ["AF_INET", 32791, ...
clientSocket = UDPSocket.new
clientSocket.connect("localhost", 9876)
clientSocket.send("Hallo, wie geht's dir?", 0)
#-> ["Hallo, wie ...", ["AF_INET", 32792, ...
clientSocket.send("Mir geht's gut.", 0)
#-> ["Mir geht's ...", ["AF_INET", 32792, ...
server.join
Wenn man Wert auf Sicherheit legt, kann man eine Ruby-Implementation
von Zebedee [Zebedee]
verwenden, um "`Tunnel"'-Datenübertragungen
per TCP/IP oder UDP zu verschlüsseln und zu komprimieren.
Mit rzbd [Rzbd] kann ein Ruby-Socket erzeugt
werden, über den verschlüsselte Daten ausgetauscht werden können.
require "rzbd/zebedee"
include Zebedee
app = Zebedee::App::instance
app.config.parse_config_file("config-file")
app.config.server_host = "server.host.name"
app.logger.loglevel = 6
app.logger.start
socket = ZBDSocket.open("target_host", portno)
socket.puts("...")
a = socket.gets