PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


UDP Nachrichten versenden und empfangen

with 7 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 , ,

7 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:
    http://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, […]

Leave a Reply

You can add images to your comment by clicking here.