next up previous contents index
Search Next: CGI-Skripte mit Ruby Up: Sockets Previous: Grundlagen   Contents   Index

Das Socket-API in Ruby

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


\epsfig{height=36pt,file=images/chan.eps}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
\epsfig{height=10pt,file=images/ruby.eps}



Footnotes

... UDP[*]
Nicht die Usenet Death Penalty

next up previous contents index
Search Next: CGI-Skripte mit Ruby Up: Sockets Previous: Grundlagen   Contents   Index
(C) 2002 by dpunkt.de, Armin Roehrl, Stefan Schmiedl, Clemens Wyss 2002-01-20