Chat via Socketserver und JavaScript Teil 1
Dass Sockets via PHP zu programmieren sind, ist mittlerweile vielen bekannt; doch dass man über einen kleinen Trick Sockets auch ohne Probleme in JavaScript nutzen kann, ist den Meisten noch weitgehend fremd. Ich habe diese Techniken bei CWidget erfolgreich verwendet und möchte hier einen kurzen Einblick in die Umsetzung geben. Im ersten Teil dieses Artikels werden wir uns lediglich mit der serverseitigen Programmierung beschäftigen.
Um einen Socket-Server effektiv betreiben zu können, ist ein Root-Server dringend notwendig, denn der Socket-Server muss kontinuierlich als Dienst laufen. Zum Testen und Entwickeln kann man ihn vorerst auch im Browser starten; doch nach einiger Zeit wird es unweigerlicher zum Stillstand des Scripts kommen.
Fangen wir mit den Grundeinstellungen unseres Mini-Chat-Servers an:
#!/usr/bin/php < ?php set_time_limit(0); ob_implicit_flush(); $address = "1.2.3.4"; $port = 1234;
Was hier genau passiert ist im Prinzip nichts Weltbewegendes; nichts desto trotz sind diese Zeilen extrem wichtig für das korrekte Arbeiten des Socket-Servers:
#!/usr/bin/php
Diese Zeile macht im Prinzip nichts außer festlegen, dass die PHP Datei in der Shell als normaler Prozess ausgeführt werden kann. Bitte passt hier auch euren Pfad zu PHP an.
set_time_limit(0);
ob_implicit_flush();
set_time_limit(0) sorgt dafür, dass der Server durchgehend aktiv bleibt, indem es die maximale Laufzeit eines PHP Scriptes ignoriert. ob_implicit_flush() hilft uns mit den Ausgaben. Hierdurch können echos und Ähnliches direkt ausgegeben werden, ohne dass das Script fertig geladen sein muss. $address und $port benötigen wir später für den Verbindungsaufbau zum eigenen Server. Die IP muss die des eigenen Servers sein und der Port kann ein gewünschter Port sein, auf dem man auf eingehende Verbindungen warten möchte.
Kommen wir nun zum nächsten Teil unseres Socket-Servers. In diesem Abschnitt werden wir die Verbindung aufbauen und auf den gewünschten Ports auf eingehende Verbindungen warten:
if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) > 0) { echo "Fehler: " . socket_strerror($master) . "\n"; exit(); } socket_set_option($master, SOL_SOCKET,SO_REUSEADDR, 1); if (($ret = socket_bind($master, $address, $port)) > 0) { echo "Fehler: " . socket_strerror($ret) . "\n"; exit(); } if (($ret = socket_listen($master, 5)) > 0) { echo "Fehler: " . socket_strerror($ret) . "\n"; exit(); }
if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) > 0) {
echo "Fehler: " . socket_strerror($master) . "\n";
exit();
}
Hier stellen wir den Master-Socket ein, über den alles laufen wird. Detaillierte Informationen zu den Einstellungsmöglichkeiten findet ihr auf php.net.
if (($ret = socket_bind($master, $address, $port)) > 0) {
echo "Fehler: " . socket_strerror($ret) . "\n";
exit();
}
Hier verbinden wir unseren Master-Socket mit dem oben definierten Port und der IP. Wie sonst sollte unser Socket wissen von wo die gewünschten Daten kommen sollen?
if (($ret = socket_listen($master, 5)) > 0) {
echo "Fehler: " . socket_strerror($ret) . "\n";
exit();
}
Jetzt weisen wir unseren Socket an auf eingehende Verbindungen zu warten. Der übergebene Parameter ‚5’ steht hierbei für einen Backlog. Dies ermöglicht bei vielen Anfragen eine interne Schleife zur weiteren Verarbeitung. Mehr hierzu auf php.net
Jetzt kommen wir zum Herzstück unseres Socket-Servers: Der Endloschleife. Da dieser Teil sehr umfangreich ist, habe ich mir erlaubt, hier auf PHP-Kommentare zurückzugreifen. Falls es von eurer Seite Einwände oder Missverständnisse geben sollte, werde ich mich darum bemühen diesen Teil nochmal abzuändern.
// Socketvariable $allsockets = array($master); // Abfragen ob die Verbindung unseres Mastersockets steht, falls nicht Server beenden. if($master && $ret) { // Endlosschleife starten while (true) { // Wir speichern die Socketliste in ein neues Array $changed_sockets = $allsockets; // Wir holen uns alle Sockets, die ansprechbar sind $num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, 1); // Wir durchlaufen alle ansprechbaren Sockets foreach($changed_sockets as $socket) { // Überprüfung ob es ein neues Socket ist if ($socket == $master) { // Wir überprüfen ob es gelingt eine Verbindung zu dem Socket aufzubauen if (($client = socket_accept($master)) < 0) { // Falls nicht, geben wir einen Fehler aus echo "fehler: " . socket_strerror($msgsock) . "\n"; continue; } else { // Verbindung wurde erfolgreich aufgebaut, wir fügen Ihn zu unserer Socketliste hinzu array_push($allsockets, $client); } } else { // Da es kein neues Socket ist, durchlaufen wir die bestehenden Sockets // als erstes lesen wir das Socket komplett aus $bytes = socket_recv($socket, $buffer, 2048, 0); // Falls 0 bytes zurückommen und im buffer keine flash policy request vorkommt, ist der client tot if ($bytes == 0 && $buffer !== '<policy-file-request/>') { // Socket in array suchen $index = array_search($socket, $allsockets); // aus socketliste löschen unset($allsockets[$index]); // verbindung zum socket schliessen socket_close($socket); } // Wir überprüfen ob es sich um ein policy-file-request handelt, welches von einem Flashsocket immer gesendet wird. Dies ist sehr wichtig für Teil 2 unseres Artikels, da wir indirekt eine Flash-Socketverbindung aufbauen werden. elseif (preg_match("/policy-file-request/i", $buffer) || preg_match("/crossdomain/i", $buffer)) { // Flash erwartet eine Antwort als XML. Hier wird die IP/Domain sowie der Port definiert. * sind auch hier als WildCard möglich. Ohne diese Antwort wird Flash keine Verbindung aufbauen. Dies gibt ein Stück Sicherheit. Flash wird bei Erfolg eine normale Socketanfrage stellen, aus diesem Grund wird diese Verbindung nach dem senden des Policy-Files wieder geschlossen. $contents='< ?xml version="1.0"?><cross -domain-policy><allow -access-from domain="DOMAIN" to-ports="PORT" /></cross>'; // xml senden socket_write($socket,$contents); // socket suchen $index = array_search($socket, $allsockets); // socket aus socketliste löschen unset($allsockets[$index]); //verbindung zerstören socket_shutdown($socket, 2); // verbindung zum socket schliessen socket_close($socket); } // falls eine verbindung via get / post oder http kommt, zerstören elseif (( preg_match("/GET/", $buffer) || preg_match("/POST/", $buffer)) && preg_match("/HTTP/", $buffer)) { if (preg_match("/favicon.ico/i", $buffer)) { //ignorieren } else { // ignorieren } // socket suchen $index = array_search($socket, $allsockets); // socket aus socketliste löschen unset($allsockets[$index]); // verbindung zerstören @socket_shutdown($socket, 2); // verbindung zum socket schliessen @socket_close($socket); } else { // wir speichern die nachricht des sockets zur weiteren verarbeitung $msg = $buffer; } // Auf valide Daten uberpruefen if(isset($msg) && !empty($msg) && trim($msg) !== '') { // überprüfen ob der socket in der socketliste vorhanden ist, falls nicht fügen wir ihn hinzu, da er neu ist if(isset($allsockets[$socket])) { // Nun senden wir das Geschriebene eines Sockets, an alle anderen Sockets foreach($allsockets as $s) { // Nachricht an Socket senden socket_write($s,$msg); } } else { // socket zur socketliste hinzufügen $allsockets[$socket] = $socket; // wir schicken dem user eine kurze verbindungsbestätigung socket_write($socket,"<strong>Erfolgreich verbunden</strong><br />"); } // nachricht löschen um speicher freizugeben unset($msg); } } } } // Dies ist extrem wichtig, damit der Server stabil laufen kann. Dadurch macht der Server bei jedem Durchlauf 10 Mikrosekunden Pause. Das ist nicht spürbar, aber entlastet den Server extrem. usleep(10); } } else { // fehler ausgeben, da server port belegt ist. echo "Port ist noch belegt."; }
Nun haben wir ein funktionsfähigen Chatserver auf PHP-Basis, doch wie gehts weiter? Dieser Chatserver ist extrem simpel und erlaubt lediglich das Kommunizieren mit Leuten die sich auch zur selben Zeit in diesem Chat befinden. Es gibt keine Channels, Usernamen o.Ä. Das ist natürlich relativ mager. Man sollte nun Authentifizierungsmethoden, Channels, Private Messaging und besonders Prozesskontrolle einbauen. Ihr wollt sicherlich nicht, dass euer Server mehrfach läuft. Hierfür gibt es einige nützliche PHP- und Linux-Funktionen.
Im nächsten Teil werde ich beschreiben, wie man via JavaScript zu solch einem ChatServer eine Verbindung aufbauen kann, um darüber zu chatten.










Februar 19th, 2009 at 19:15
[...] the original: Chat via Socketserver und JavaScript Teil 1 Related ArticlesBookmarksTags There are no related articles. Digg it Stumble [...]
Mai 2nd, 2009 at 22:00
Nabend. Eine etwas unpassende Frage aber wie kann ich diesen Blog zu meinem Google Feedreader hinzufuegen? Finde keinen Link. Auf jedenfall ein toller Beitrag.