PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


UDP Nachrichten versenden und empfangen

with 12 comments

Das Transport Protokol UDP ist der kleine Bruder von TCP. UDP ist nicht verlässlich, die Reihenfolge der Pakete ist beim Empfänger eventuell eine andere als beim Absender und es gibt nur eine Fehler-Erkennung, aber keine Fehler-Korrektur. Doch UDP bietet auch Vorteile: Es ist deutlich schneller als TCP (schneller meint hier dass die Pakete schneller beim Empfänger sind), es muss kein aufwändiger Handshake durchgeführt werden, es werden insgesamt weniger Ressourcen verbraucht.

Ein kurzes Beispiel, wie ein UDP Client aussieht, der einfach die IP-Adresse des aktuellen Webbesuchers an einen Server schickt:

<?php
$socket = fsockopen('udp://192.168.1.33:10000');
fputs($socket, $_SERVER['REMOTE_ADDR']);

So einfach kann es sein. Dies speichern wir als client.php auf unserem Webserver und lassen beispielsweise Apache Bench laufen:

ab -k -n 2000 -c 50 http://udp.localhost/client.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking udp.localhost (be patient)
Completed 200 requests
Completed 400 requests
Completed 600 requests
Completed 800 requests
Completed 1000 requests
Completed 1200 requests
Completed 1400 requests
Completed 1600 requests
Completed 1800 requests
Completed 2000 requests
Finished 2000 requests

Server Software:        Apache/2.2.12
Server Hostname:        udp.localhost
Server Port:            80

Document Path:          /client.php
Document Length:        0 bytes

Concurrency Level:      50
Time taken for tests:   2.747 seconds
Complete requests:      2000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    2000
Total transferred:      540050 bytes
HTML transferred:       0 bytes
Requests per second:    728.03 [#/sec] (mean)
Time per request:       68.679 [ms] (mean)
Time per request:       1.374 [ms] (mean, across all concurrent requests)
Transfer rate:          191.98 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    13   68   4.6     68      86
Waiting:       13   68   4.6     68      86
Total:         13   68   4.5     68      86

Percentage of the requests served within a certain time (ms)
  50%     68
  66%     69
  75%     70
  80%     71
  90%     72
  95%     73
  98%     75
  99%     76
 100%     86 (longest request)

Wir können also ungefähr 700 Requests pro Sekunde abfeuern. Aber halt, wohin werden die UDP-Pakete verschickt? Richtig, sie gehen ins Nirwana, es läuft noch kein entsprechender Server, und da UDP sich nicht darum kümmert ob die Pakete ankommen oder nicht (fire and forget) funktioniert es. Würden wir TCP verwenden, wäre unser Skript sehr langsam, es würde versuchen eine Verbindung aufzubauen, und nach X Sekunden würde der Versuch abgebrochen falls kein Server gefunden wird, und es hagelt Fehlermeldungen.

In diesem Fall wäre es uns egal falls der Server (Sammler) nicht läuft, denn es sollen nur IP-Adressen geloggt werden oder beispielsweise soll ein kleines Reporting-Tool einige Statistiken sammeln. Die Daten sind nicht so wichtig als dass die Webseite darunter leiden soll wenn der Reporting-Server mal nicht erreichbar ist.

Wie sieht ein einfacher Server aus?

<?php
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);

if (!$socket) die('Unable to create socket');
if (!socket_bind($socket, null, 10000)) die('Unable to bind socket');

while(true) {
    $data = @socket_read($socket, 15);

    echo $data . "\n";  // oder in eine Datei schreiben
}

10000 ist der Port, auf dem wir die Nachrichten empfangen wollen. null bedeutet hier dass wir auf allen Interfaces lauschen wollen, hier könnte man auch nur bestimmte Adressen (bspw: 192.168.1.33 oder localhost) nehmen. Da es keine Authentifizierung gibt sollte natürlich darauf geachtet werden wo man den Port öffnet. Die 15 bedeutet dass wir maximal 15 Byte erwarten, da eine IPv4-Adresse nicht länger sein kann. Sollen beliebig große Nachrichten empfangen werden muss dies erhöht werden.

Written by Michael Kliewe

März 2nd, 2011 at 7:52 am

Posted in PHP

Tagged with , ,

12 Responses to 'UDP Nachrichten versenden und empfangen'

Subscribe to comments with RSS or TrackBack to 'UDP Nachrichten versenden und empfangen'.

  1. Schönes Beispiel.

    Aber sollte in der while(true) Schleife nicht noch
    ein sleep eingebaut werden, damit der Server nicht ständig auf 100% Last läuft? Oder ist das in PHP anders?

    webthief

    3 Mrz 11 at 17:26

  2. @webthief: In diesem Fall braucht man das nicht, da der socket_read() Aufruf blockierend ist, sprich PHP bleibt an dieser Stelle stehen bis Daten kommen.

    Michael Kliewe

    3 Mrz 11 at 18:04

  3. ah, gut zu wissen
    danke

    webthief

    5 Mrz 11 at 15:11

  4. […] PHP: UDP-Nachrichten versenden und empfangen. […]

  5. Hi,

    interessante Möglichkeit. Da der Server aber keine mehreren Child-Prozesse für jede Anfrage öffnet…
    Wieviele von den 2000 schnell hintereinander abgegebenen Request hätte der denn tatsächlich auch verarbeitet?

    Axel

    11 Mrz 11 at 19:21

  6. @Axel: Der Server hat die Nachrichten glaube ich alle verarbeitet, ich habe sie zum Test nur auf der Console ausgegeben und nicht weggeloggt (siehe Script oben).

    Besser wäre evtl, wie du schon sagst, ein non-blocking Server ähnlich wie hier, um das ganze etwas besser zu skalieren:
    https://www.phpgangsta.de/wie-erstelle-ich-einen-socket-server-in-php

    Michael Kliewe

    12 Mrz 11 at 11:28

  7. […] UDP-Library UDP Server und Client mit PHP Gefällt mir:Gefällt mirSei der Erste dem dies gefällt. Dieser Beitrag wurde unter Arduino, […]

  8. Hi,

    wenn ich den Socket geöffnet habe, wie kann man ihn wieder schließen?

    Philipp

    21 Aug 17 at 12:55

  9. @Philipp: Du meinst wenn du den Socket mit socket_create() erstellt hast?

    http://php.net/socket_create

    Rechts in der Liste, oder unten in den Kommentaren steht die Antwort: socket_close($socket);

    Michael Kliewe

    21 Aug 17 at 13:00

  10. aber wenn er permanent lauschen soll und ich ihn erst wieder irgendwann schließen möchte?

    https://www.php.de/forum/webentwicklung/php-einsteiger/1511615-php-socket-open-close

    Philipp

    21 Aug 17 at 13:08

  11. @Philipp: Ein Socket wird spätestens geschlossen wenn das PHP-Script beendet wird. Das passiert automatisch.

    Wenn du den Socket „irgendwann“ schliessen möchtest, dann musst du an die Stelle im Code, wann „irgendwann“ ist, socket_close() aufrufen. Wenn also irgendein externes Ereignis auftritt, oder eine bestimmte Nachricht gesendet wurde (z.B. QUIT) oder sowas.

    Bist du „horphi“, der Fragensteller?

    lstegelitz hat recht, du hast das Prinzip von HTTP noch nicht verstanden. Wenn du einen HTTP-Befehl zum Starten schickst, dann läuft der Socket-Server in einer Endlosschleife, als Apache-PHP-Prozess. Allerdings bricht der Socket-Server nach meistens 30 Sekunden ab und beendet sich, weil PHP via Apache gestartet wurde, und je nach max_execution_time wird dein Socket-Server gekillt.

    Lang laufende PHP-Prozesse müssen immer auf der Kommandozeile laufen, da sie ja über Stunden, Tage und Monate laufen sollen. Das geht mittels eines Apache-PHP-Prozesses nicht. Deshalb startet man Server (Daemons) immer auf der CLI Kommandozeile.

    Du kannst den so gestarteten Socket-Server dann natürlich mittels kleiner HTTP-Befehle von außen steuern, in dem du einen HTTP-Request via Apache zum Server schickst, und der Apache-PHP-Prozess sagt dem CLI-PHP-Prozess dass er stoppen soll. Zum Beispiel in dem er eine Datei „stop.txt“ in den Ordner legt, und der Socket-Server jede Sekunde einmal prüft ob es die Datei gibt. Oder aber man sendet eine spezielle Nachricht an den Socket-Server („QUIT“), und er beendet sich darauf hin.

    Michael Kliewe

    21 Aug 17 at 13:25

  12. Hi,

    Fragesteller: ja

    Danke fürs Feedback.

    Also dann eher nach deiner Methode. 😉

    https://www.phpgangsta.de/wie-erstelle-ich-einen-socket-server-in-php

    Philipp

    21 Aug 17 at 13:48

Leave a Reply

You can add images to your comment by clicking here.