Ruby-Programmierung: Netzwerkprogrammierung
Zurück zum Inhaltsverzeichnis.
Mit der Hochverfügbarkeit von Internet seit dem letzten Jahrtausendwechsel bekamen auch alle Programme die Chance die Vernetzung zwischen Nutzern und Computern zu nutzen. Das erlaubt Ihnen als Programmentwickler beispielsweise einfache Updatemethoden oder Feedbackmachnismen zu implementieren.
Trotz der neuartigen Verfügbarkeit des Internets ist die zugrundeliegende Technologie bereits sehr alt. So bauen alle Netzwerkverbindungen heutzutage auf der Berkley Socket API für die Programmiersprache C auf, die in den siebziger Jahren entwickelt wurde. Daher wundert es nicht, das die Benutzung der Socketklasse in Ruby zwar in den meisten Fällen eine sehr elegante Lösung erlaubt, in einigen Grenzfällen jedoch Merkwürdigkeiten aufzeigt.
Sockets und Protokolle und ...
BearbeitenBei der Verwendung von Netzwerken und insbesondere in diesem Kapitel sind und werden relativ viele Bezeichnungen benötigt, die dem einsteigenden Leser vielleicht ohne weitere Recherche überfordern würden, daher dient dieser Abschnitt dazu die einzelnen Stichworte kurz zu erläutern und sie miteinander in Kontext zu setzen.
- Sockets sind bidirektionale Verbindungen, die es erlauben Daten zwischen den beiden Endpunkten der Verbindung austauschen. Dies können Netzwerkverbindungen sein, oder UNIXSockets, die zwei Programme verbinden.
- Protokolle sind Vereinbarungen zwischen den beteiligten Parteien (Computer, Netzwerkinfrastruktur) über die Art und Weise der Kommunikation
- IP das internet protocol ist vor allem in Verbindung mit der IPNummer wichtig, die jeden Rechner in einem Netzwerk eindeutig addressiert und ihn so für andere Rechner erreichbar macht.
- TCP ist ein Protokoll darüber, wie die übermittelten Daten aussehen, insbesondere erzwingt TCP das Erreichen der Daten beim Kommunikationspartner in der richtigen Reihenfolge. Im Gegensatz dazu stellt UDP keine solche Funktionalität bereit und eignet sich daher besser für Echtzeitanwendungen.
- Client-Server-Modell ist eine hierarchische Methode mehrere Computer miteinander zu verbinden. Jeder Client verbindet sich zu einem Server, der die Kommunikation verwaltet. Dabei erfolgt jede Kommunikation zwischen den Clients über den Server. Im Gegensatz dazu verwalten die Kommunikation beim peer-to-peer die Programme selbst.
Chat
BearbeitenUm die recht theoretischen Betrachtungen des letzten Abschnitts in einen praktischen Kontext zu setzen, sollen zwei Programme entwickelt werden, die einen Chat zwischen beliebig vielen Personen erlaubt.
Um eine korrekte Übermittlung von Daten auf der Netzwerkseite zu verbessern bietet sich TCP als Protokoll an. Da beliebig viele Nutzer erlaubt sein sollen bietet sich ein Client-Server-Modell an, sodass ein Server die Kommuonikation zwischen allen verbundenen Clients verwaltet. Der Server wird außerdem für jede Verbindung mit einem Client einen Thread benötigen, um den entsprechenden Socket zu verwalten.
Client
BearbeitenNach diesen Vorüberlegungen widmen wir uns zunächst der Clientseite.
require "socket"
host = 'localhost'
port = 2000
sock = TCPSocket.open(host, port)
puts "A Simple Chat"
# recieving messages
Thread.new { loop { puts sock.gets } }
# sending messages
catch :exit do
loop do
input = gets.chomp
case input
when "!exit", "!e"
sock.puts input
throw :exit
else
sock.puts input
end
end
end
sock.close
Das Programm besteht im wesentlichen aus drei Teilen: Initialisierung, Empfang und Senden.
Beim Initialisieren öffnen wir eine Verbindung über den Netzwerkstack des Betriebssystem zum eigenen Rechner an Port 2000. Das es sich nicht über eine wirkliche Verbindung über ein Netzwerk zu einem anderen Rechner handelt ist dabei irrelevant, da durch Austauschen der IP eine Verbindung hergestellt werden kann.
Das Empfangen von Nachrichten soll zeitlich unabhängig vom Senden funktionieren, daher benötigt der Client eine eigene parallel ausgeführte Schleife zum Empfangen. Die Nachrichten werden einfach ausgegeben.
Das Senden funktioniert analog zum Empfangen mit dem Unterschied, das jetzt auf den Socket geschrieben wird anstatt ihn zu lesen. Beendet wird das Programm mit !e
oder !exit
.
Server
Bearbeitenrequire "socket"
class User
attr_reader :name, :sock
def initialize(name, sock)
@name = name
@sock = sock
end
end
port = 2000
server = TCPServer.new(port)
users = []
loop do
Thread.start(server.accept) do |sock|
sock.puts "Please enter a name."
name = sock.gets.chomp
user = User.new(name, sock)
users << user
loop do
msg = sock.gets.chomp
case msg
when "!exit", "!e"
users.delete user
else
users.each do |u|
u.sock.puts "#{ user.name }: #{ msg }"
end
end
end
end
end
Da viele Sachen Ihnen bekannt vorkommen sollten, wird nur näher auf die eigentliche Serverloop eingegangen. Bei jeder eingehenden Verbindung server.accept
wird ein neuer Thread gestartet, der die Initialisierung dieser Verbindung übernimmt. Der Nutzer wird nach einem Namen gefragt und dieser zusammen mit seinem Socket in einem Array gespeichert. Bei einer eingehenden Nachricht wird überprüft ob es sich um das ausloggen des Nutzers handelt (!e
) oder um eine gewöhnliche Nachricht, diese wird an alle Nutzer weitergeleitet.
Das interessante an der Verwendung von Netzwerken ist, dass Sie sehr wenig neues in diesem Kapitel gelernt haben. Hätten Sie vordem Lesen dieses Kapitels die Quelltexte überflogen, dann hätten Sie bis auf zwei, drei Zeilen keinen Code entdecken können der Ihnen unbekannt vorkommt. Wichtig bei der Verwendung der Verbindung zwischen zwei Rechnern ist weniger die Tatsache, sondern was das Programm damit anstellt.
HTTP
BearbeitenIn der Einleitung wurde über das Internet gesprochen, dann jedoch nicht weiter erwähnt. TCP ist ein Netzwerkprotokoll und hat mit dem Internet grundlegend sehr viel zu tun. Mehr jedoch mit HTTP, dem Protokoll mit dem die meisten Dateien (zum Beispiel Webseiten) im Internet übertragen werden.
Die Bibliothek Net::HTTP unterscheidet sich ganz grundlegend von den oben kennengelernten Sockets, da zwar die Kommunikation über die Sockets läuft, aber HTTP keine Verbindungen kennt. Der Client schickt eine Anfrage an den Server und dieser Antwortet danach, es wird in seiner einfachen Form keine Verbindung aufrecht erhalten, sondern jede Abfrage muss über eine neue Verbindung ausgeführt werden.
Ein einfacher Browser
BearbeitenHELP = "VSB - very simple browsing
!exit, !e --- exits the program
!help, !h --- prints this help
Anything else is assumed as URL."
def sanitize(input)
return input if input =~ /http:\/\//
"http://#{ input }"
end
require "uri"
require "net/http"
input = ""
catch :exit do
loop do
puts "VSB - very simple browsing - try !help"
print "CMD: "
input = gets.chomp
content = ""
case input
when "!e", "!q", "!exit", "!quit"
throw :exit
when "!h", "!help"
puts HELP
else
input = sanitize(input)
uri = URI(input)
content = Net::HTTP.get(uri)
content.gsub!(/<.+?>/, "").gsub!(/\s+/, " ")
puts content
end
end
end
Dieser sehr einfache Browser nutzt die Bibliotheken Net::HTTP
, um eine Website zu besuchen und URI
um eine gegebene Adresse zu parsen. Der HTMLCode der entsprechenden Seite wird danach etwas aufbereitet und angezeigt.
Die Methode sanitize
garantiert das am Anfang der Adresse das Protokoll angegeben wird, dies ist notwendig, um dem Nutzer auf die Engabe des Protokolls zu verzichten.