PowerShell und UDP

Diese Seite beschäftigt sich primär mit der UDP-Client-Klasse, mit der Sie mit PowerShell relativ einfach UDP-Pakete senden und empfangen können. Diese Fähigkeit hilft z.B.: bei:

  • Simulieren von VoIP Paketen und Lasten
  • Lasttest (Fluten) durch sehr viele und schnelle UDP-Pakete ohne Gegenstelle (Send Only)
  • Versand von SYSLOG-Meldungen
  • IoT-Meldungen einsammeln
    Kleine Sensoren melden nur Messwerte und oft ist der TCP-Overhead und die Konfigurationen von Zielen zu groß. Im gleichen Subnetz kann ein UDP-Broadcast helfen, der dann per PowerShell eingefangen wird.

Natürlich können Sie auch Prozesse und Skripte über UDP-Broadcasts miteinander sich finden lassen und einige 3rd Party Produkten senden auch Daten per UDP in die Welt

UDP-Paket senden

Es dauert nur wenige Minuten den Code zum Senden eines UDP-Pakets fertig zu haben. Hier ein Auszug des Skripts:

# send-udp
#
# Simple Tool to send UDP-Ports to a range with a various source port range.
# you can use Netmon/Wireshark on the target to see the incomping traffic.
# i have NO listener to listen on all ports
#
#
# Links: http://pshscripts.blogspot.de/2008/12/send-udpdatagramps1.html

param (
   [string]$remoteip = "192.168.100.1", # IP to send to 
   [int]$remoteudpport=50000,           # port to send to
   [int]$sourceudpport = 0,             # SourcePort, maybe empty uses and available port
   [string]$buffer = "SendUDP Message by msxfaq"
)

$udpClient = new-Object system.Net.Sockets.Udpclient($sourceudpport) 
$byteBuffer  = [System.Text.Encoding]::ASCII.GetBytes($Buffer)
$sendbytes = $udpClient.Send($byteBuffer, $byteBuffer.length, $remoteip, $remoteudpport)
if ($sendbytes -ne $byteBuffer.length) {
   write-host "Mismatch bytes"
}

PowerShell und .NET ist schon genial, wie man mit ganz wenigen Zeilen selbst solche Basisfunktionen umsetzen kann. Und wenn Sie die Parameter entsprechend anpassen, können Sie damit schon ein Netzwerk fluten.

Hier das komplette Skript als Muster:

send-udp.1.0.ps1

Es ist nicht möglich, auf dieser Ebene auch noch QoS-Tags, also DSCP-Tags zu setzen. Das können Sie aber einfach mit Windows Bordmitteln machen. Es gibt aber auch noch die Socket-Schnittstelle, die ebenfalls verwenden werden kann. Sie ist aber etwas umfangreicher. Hier ein Codeauszug:

$IPAddress = [system.net.IPAddress]::Parse($targetip) 
$AddressFamily = [System.Net.Sockets.AddressFamily]::InterNetwork 
$SockettypeDatagram = [System.Net.Sockets.SocketType]::Dgram 
$ProtocolTypeUDP = [System.Net.Sockets.ProtocolType]::UDP 
$Buffer = [System.Text.Encoding]::ASCII.GetBytes("UDPTest MSXFAQ") 

$Sock     = New-Object System.Net.Sockets.Socket $AddressFamily, $SockettypeDatagram, $ProtocolTypeUDP
$Sock.TTL = 100  
$remotesystem = New-Object System.Net.IPEndPoint $IPAddress, $port
$sock.Connect(remotesystem)  
$Sent = $Sock.Send($Buffer)

System.Net.Sockets.Socket
http://msdn.microsoft.com/de-de/library/vstudio/system.net.sockets.socket

Qualitätskontrolle mit NetMon

Der einfaches Weg die Funktion zu verifizieren  ist ein Mitschnitt durch NetMon. Hier habe ich über eine WiFi-Verbindung das Paket einmal versendet.

Der Code funktioniert und der ausführende Prozess muss dabei nicht einmal Administrator sein.

UDP und ICMP not Reachable

Im Gegensatz zu TCP gib es bei UDP keinen Handshake mit Retransmit. Die Übertragung von Daten per UDP ist daher ungesichert gegen Verluste. Senden Sie also nur unkritische Daten, deren Zustellung nicht erforderlich ist. Zudem habe ich noch nie gesehen, das eine Gegenstelle. die auf dem Port nicht erreichbar ist ein "ICMP not reachable" zurück sendet. Ein UDP-Paket ist also eher wie eine Flaschenpost. Sie können prüfen, ob sie versendet wurde, aber solange niemand darauf antwortet, können Sie den Status der Zustellung nicht überprüfen. Es gibt aber doch den ein oder anderen "ICMP not Reachable". Den senden nämlich Router, wenn der TTL abgelaufen ist. Damit können Sie per UDP auch einen "Traceroute" machen. Windows nutzt dazu ICMP PING aber UNIX-Systeme nutzen dazu auch UDP. Das geht mit UDP auch unter Windows.

$udpClient = new-Object system.Net.Sockets.Udpclient($sourceudpport) 
[string]$buffer = "SendUDP Message by msxfaq"
[int]$remoteudpport=3478
[string]$remoteip = "13.107.8.2"
$byteBuffer = [System.Text.Encoding]::ASCII.GetBytes($Buffer)

1..10 | %{
   $udpclient.ttl = $_;`
    start-sleep -Milliseconds 400; `
   $sentbytes = $udpClient.Send($byteBuffer, $byteBuffer.length, $remoteip, $remoteudpport)`
}

Die Verzögerung mit 400ms habe ich extra gemacht, dass die Antworten in Wireshark auch in der richtigen Abfolge sind. Ansonsten würden die 10 UDP-Pakete schnell hintereinander rausgehen.

Damit bleibt nur noch die Frage, wie ich per Software die ICMP Rückmeldung erfasse. Der UDP-Client liefert hier nämlich keine direkte Exception bei einem "Time-to-live exceeded". Allerdings gibt es durchaus eine Exception, denn die Gegenseite ein "ICMP Port not Reachable" wirft.

Zeitverhalten

Für ein später geplantes VoIP-Monitoring muss ich aber mehrere Pakete senden. Das geht per For-Schleife ehr einfach aber wichtig ist dabei, dass die Pakete wirklich mit dem gleichen Zeitabstand und in der geplanten Reihenfolge versendet werden. Also habe ich eine FOR-Schleife drum herum gebaut.

for ($i=1; $i -lt $repeat; $i++) {
   $byteBuffer  = [System.Text.Encoding]::ASCII.GetBytes(($Buffer + [string]($i)))
   $sentbytes = $udpClient.Send($byteBuffer, $byteBuffer.length, $remoteip, $remoteudpport+$i)
   if ($sentbytes -ne $byteBuffer.length) {
      write-host "Send Bytes Mismatch"
   }
   start-sleep -milliseconds $throttle
}

Abweichend zum normalen Code habe ich hier den "RemoteUDPPort" mit dem Counter erhöht, um im Wireshark gleich anhand des Ports die "Reihenfolge" zusehen.

Und hier ist zu sehen, dass die Pakete in ca.10-11ms-Abständen gesendet werden. Eine gewisse Streuung liefert also schon der Sender. Ob das nun an PowerShell, Windows oder vielleicht an einem anderen Paket liegt, das gerade versendet wird und daher mein Paket etwas warten muss, ist nicht bestimmbar. Ich habe den Test per WiFi (135MBit) gemacht, was sicher auch noch etwas Varianz mit rein bringt.

UDP empfangen

Als nächstes steht die Aufgabe an, eine UDP-Nachricht zu empfangen. Auch das geht mit PowerShell und NET-Bordmitteln.

Achtung:
Vergessen Sie nicht auf der Windows Firewall eine Regel zu addieren, um den Port eingehend zuzulassen. Wireshark und Netmon sehen die Pakete vor der Windows Firewall und zeigen diese auch an. Mit aktiviertem UAC kann Windows auch ein Popup starten, welches die Freigabe erbittet. Auf Servern ist das aber eher nicht der Fall. Es hilft auch nicht das Skript dann als Administrator zu starten.

Ein Auszug aus dem Source zeigt die Basisfunktion, um auf einem Port zu lauschen und die empfangenen Pakete zurück zu geben:

# receive-udp

param (
   [string]$localip = "0.0.0.0",
   [string]$udplistenport=1514
)

$udpClient = New-Object system.Net.Sockets.Udpclient($udplistenport)
$RemoteIpEndPoint = New-Object System.Net.IPEndPoint([system.net.IPAddress]::Parse($localip)  , $udplistenport);

while ($true) {
   Write-host "Receive-UDP:Wait für Data on Port: $udplistenport"
   $data=$udpclient.receive([ref]$RemoteIpEndPoint)

   write-host "Received packet from IP " $RemoteIpEndPoint.address ":" $RemoteIpEndPoint.Port
   write-host "Content" ([string]::join("",([System.Text.Encoding]::ASCII.GetChars($data))))
}

Knifflig ist, dass Windows selbst keinen Zeitstempel anfügt aber die Pakete puffert. Die Funktion kommt zurück, wenn ein Paket angekommen ist oder schon eins im Puffer ist. Wer also möglichst genau den Zeitpunkt des Empfangs benötigt, muss sehr zeitnah die Daten auslesen. Wenn ich auf dem gleichen PC die Daten per "localhost" sende und empfange, zeigen sich schon größere Unterschiede. Wobei hier sicher auch die Bildschirmausgabe einen Einfluss haben kann.

PS C:\> .\receive-udp.ps1 | select timestamp
Receive-UDP:Wait für Data on Port: 50002

timestamp
---------
2013.10.24 14:25:31.500
2013.10.24 14:25:31.521
2013.10.24 14:25:31.530
2013.10.24 14:25:31.550
2013.10.24 14:25:31.570
2013.10.24 14:25:31.580
2013.10.24 14:25:31.600
2013.10.24 14:25:31.610
2013.10.24 14:25:31.630
2013.10.24 14:25:31.640
Receive-UDP:Closing

Es sind alle 10 gesendeten Pakete angekommen aber die Abstände sind doch zumindest schwankend. Ich gehe mal davon aus, dass sie relativ linear gesendet wurden. Hier das komplette Skript als Muster:

receive-udp.1.0.ps1

UDPClient.Client.ReceiveTimeout

Wenn Sie beim "Receive()" nicht endlos warten wollen, dann können Sie einen Timeout auf dem Socket setzen. Nach der Zeit kommt der Aufruf dann wieder zurück. Allerdings sollten Sie natürlich den Fehler abfangen.

Ausnahme beim Aufrufen von "Receive" mit 1 Argument(en):  "Ein Verbindungsversuch ist fehlgeschlagen, da die
Gegenstelle nach einer bestimmten Zeitspanne nicht richtig reagiert hat, oder die hergestellte Verbindung war
fehlerhaft, da der verbundene Host nicht reagiert hat"

Am einfachsten geht das per Try/Catch

$udpClient = New-Object System.Net.Sockets.Udpclient  #without IP address to set additional options
$udpclient.ExclusiveAddressUse = $false
$udpclient.Client.ReceiveTimeout = 1000
$LocalIpEndPoint = New-Object System.Net.IPEndPoint([system.net.ipaddress]::any), $listenport);  geht wohl nicht
$udpClient.Client.Bind($LocalIpEndPoint) 
$RemoteIpEndPoint = New-Object System.Net.IPEndPoint([system.net.ipaddress]::any,0);

while ($true) {
   # wait for data arriving from any ip. Alternativ kann man eine Liste der erlaubten IPs angeben
   $data=$null
   try {
      $data= $udpclient.receive([ref]$RemoteIpEndPoint)
   }
   catch {
      write-host "Timeout"
   }
   if ($data) {
      $receivetimestamp = get-date -format o
      write-host ""
      write-host "Paket received at $($receivetimestamp)"
      Write-host "Sender: $($RemoteIpEndPoint.tostring())"
      Write-host "---- DATA ------"
      [string[]]$payload =([system.text.encoding]::ASCII.GetString($data))  -split "`r`n"
      $payload
   }
}

Schöner wäre es natürlich, die empfangenen Dateien im Hintergrund durch einen anderen Thread verarbeiten zu lassen.

UDP Asynchron

Wer aber nicht die Ausführung des Skripts durch das Warten auf das nächste UD-Paket blockieren will, der kann auch den Empfang asynchron regeln. Das ist überraschend einfach, wenn Sie ein paar Grundlagen beherrschen. Hier erst einmal der Basiscode, der einen UDP-Port 50000 öffnet und einen Empfang startet. Ob etwas empfangen wurde, kann mit dem Property "IsCompleted" geprüft werden.

Hinweis:
Rufen Sie die im Beispiel verwendete Variable "$udptask" nicht direkt ab. Der Code bleibt dann stehen, bis ein Paket kommt. Sie müssenn $udptask.completed auswerten und wenn hier ein $true kommt, ist das Objekt erst komplett nutzbar.

In der Zwischenzeit kann der Code aber andere Dinge parallel machen. Er sollte aber ab und nach wieder nachschauen, denn dieses Beispiel ist nicht Event-gesteuert. Es wird beim Empfang also nicht das aktuelle Skript angehalten um eigenen Code auszuführen.

$udpClient = New-Object system.Net.Sockets.Udpclient(50000)
$udptask = $udpclient.receiveasync()

while (![console]::KeyAvailable ) {
   write-host "Hier kann ich was tun"
   if ($udptask.iscompleted) {
      Write-Host "UDP-Paket empfangen"
      Write-Host "SenderIP  : $($udptask.Result.RemoteEndPoint.address)"
      Write-Host "SenderPort: $($udptask.Result.RemoteEndPoint.Port)"
      [string]$udppayload =([string]::join("",([System.Text.Encoding]::ASCII.GetChars($udptask.Result.buffer))))
      write-host "Data $($udppayload)"
   }
   write-host "Hier kann ich was tun"
   start-sleep -milliseconds 100
}
$udpClient.close()

Allerdings sollten Sie noch zusätzlich prüfen, ob das Property "isfaulted" nicht auch auf True steht. Dann ist ein Fehler aufgetreten. Die hier im Beispiel verwendete Variable "udptask" enthält folgendes.

Das "udptask"-Objekt ist vom Type "System.Threading.Tasks.Task" und enthält sowohl die Verbindungsdaten des Absenders aber auch die übersendeten Daten. Ich finde die Lösung sogar deutlich eleganter als das statische Warten auf ein Paket. Nur wer seinen Code wirklich "anhalten" will, bis ein Paket kommt, ist mit der ersten Version am Anfang der Seite besser beraten. "Schneller" ist das warten aber nicht. Ich habe mir dazu folgendes Testskript gebaut, welches auf eingehende Pakete wartet, dann die Zeit merkt und ein paket an sich selbst "Localhost" sendet und in einer Schleife permanent darauf wartet. Sobald der asynchrone Task bereit steht, gebe ich die Zeit aus.

Die Antwortzeiten bewegen sich immer im Bereich von 1ms oder weniger. Sobald ich zur Fehlersuche hier eine Bildschirmausgabe aktiviere oder den $udptask in die Pipeline schreibe und erst dann die Zeit messe, bin ich bei 15-25ms. Write-Host ist wirklich ein Performance-Killer. Dennoch sollten Sie hiermit keine Werte mit einer Auflösung von wenigen Millisekunden.

UDP Buffer und Overrun

Pakete, die eintreffen während sie noch am Verarbeiten des vorherigen Pakets sind, werden nicht vergessen. Windows selbst hat einen Buffer und hält diese Pakete vor. Beim nächsten Abruf von "$udpclient.ReceiveAsync()" wird einfach das nächste Paket aus dem Buffer abgeholt.

Wenn Sie direkt nach dem "$udptask=$udpclient.ReceiveAsync" auf das Property "IsComplete" prüfen, kann es noch auf "$False" stehen. Es dauert ein paar Millisekunden , bis das Task-Objekt mit einem UDP-Paket aus dem Buffer befüllt wurde.
Damit eignet sich dieses Verfahren nicht für genaue Messungen von Laufzeiten.

Damit stellt sich aber die Frage, wie groß dieser Buffer ist. Ich habe daher einen UDPClient instanziert und von einem anderen UDPClient Pakete dort hin gesendet.

# Sample um UDP Buffer zu ermitteln

$udpreceiver = New-Object system.Net.Sockets.Udpclient(50001)
$udptask = $udpreceiver.receiveasync()

$udpsender = New-Object system.Net.Sockets.Udpclient
Write-host "Sende 100000 Pakete a 1 Byte"
1..10000 | foreach-Object {
    write-host "S" -nonewline
    $null=$udpsender.send(1,1,"127.0.0.1",50001)
}
Write-host " Done"

$count=0
$totalbyte=0
while ($udptask.iscompleted) {
    $count++
    Write-Host "E" -NoNewline
    $totalbyte += $udptask.result.buffer.Count
    $udptask = $udpreceiver.receiveasync()
    Start-Sleep -Milliseconds 100  #give threating some time
}

Write-Host "TotalPakets:$($Count)"
Write-Host "TotalBytes :$($totalbytes)"

$udpreceiver.close()
$udpsender.close()

Dabei habe ich auf meinem Windows 10 (1803)-Client herausgefunden, dass mein Betriebssystem ca. 50kByte für empfangene UDP-Pakete vorhält. Es haben ca. 50.000 Pakete mit einer Payload von 1 Bytes in den Buffer gepasst oder 10.000 Pakete a 50 Bytes. Wenn Pakete verworfen wurde, dann bekommen Sie am Ende die folgende Exception:

Exception calling "ReceiveAsync" with "0" argument(s): "An operation on a socket could not be performed because the
system lacked sufficient buffer space or because a queue was full"

UDP und Connect Methode

Interessanterweise gibt es bei de, UDP-Client auch eine "Connect"-Methode, obwohl es mit UDP genau genommen gar keine Konversation gibt. Die Connect-Methode ist aber ein Weg die entfernte Gegenstelle und den Port der Gegenstellt vorzugeben. Man kann sich dann beim Senden die Angabe dieser Daten ersparen. Viel wichtiger ist aber, dass dann auch nur Pakete von dieser Quelle angenommen werden. Sie müssen sich in ihrem Code also nicht mit "Fremdpaketen" rumschlagen, die auch auf diesen UDP-Port zugestellt werden. Wobei eine Fälschung der Quell-Adressen mit UDP natürlich sehr einfach ist.

Aber ein $UDPClient.Send(buffer,size,RemoteIP,RemotePort) an einen anderen Host durch Angabe der Ziel-IP und Port geht dann ebenfalls nicht mehr.

# Versand und Empfang mit beliebigen Zielen mit dem gleichen UDP-Client
$udpClient = new-Object system.Net.Sockets.Udpclient($sourceudpport) 
[void]$udpClient.Send($byteBuffer, $byteBuffer.length, $remoteip, $remoteudpport)

# Versand und Empfang über genau eine vordefinierte Gegenstelle
$udpClient = new-Object system.Net.Sockets.Udpclient($sourceudpport) 
$udpclient.Connect( $remoteip, $remoteudpport)
[void]$udpClient.Send($byteBuffer, $byteBuffer.length)

Allerdings soll es durch den "Connect" möglich sein, dass man ICMP-Meldungen zuordnen kann, d.h. wen ein UDP-Paket nicht ankommt, weil eine Firewall oder Server ein "ICMP not reachable" zurückliefert oder aufgrund von langen Wegen ein "TTL expired" erfolgt. soll man dies ermitteln können. Mir ist das aber leider noch nicht gelungen.

UDP und Close-Methode

Der Einsatz von "Close" am Ende eines Scripts oder im Rahmen einer Fehlerbehandlung sollten sie sich angewöhnen. Wenn Sie eine neue PowerShell starten und darin ein UDPClient-Object mit einem Port instanzieren, dann bleibt der Port auch nach dem Ende des Skripts geöffnet aber kann nicht mehr weiter verwendet werden. Ihr Script ist ja beendet. Erst wenn Sie die PowerShell selbst schließen, wird auch das UDP-Objekt aufgeräumt. Das stört natürlich extrem die Entwicklungsarbeit.

Wenn Sie das UDPClient-Object mit der Methode CLOSE() beendet, dann werden alle Ressourcen freigegeben. Die PowerShell-Variable enthält natürlich noch weiterhin ein UDP-Client-Objekt aber sie können es nicht weiter verwenden.

Vermeiden Sie daher das Abbrechen eines Skript mit "CTRL-C", da die UDP-Objekte damit aktiv bleiben und die Ports blockieren.

Ich habe aber schon gesehen, dass manche PCs weitere auf diesen Port eingehende Pakete mit einem "ICMP Port Not Reachable" ablehnen

UDP Echo

Kombiniert man nun den UDP-Empfänger und den UDP-Sender, dann lässt sich ein ECHO-System allein mit PowerShell realisieren. Dieses Skript wartet auf Pakete auf dem angegebenen Port und sendet diese direkt zurück.

echo-udp.1.0.ps1

Achtung
Dies ist ein klassischer ECHO-Server der durchaus auch missbrauch werden kann. Bei UDP gibt es keinen Handshake und so können falsche Absenderadressen nicht erkannt werden. Ich kann also eine dritte Station mit UDP-Paketen fluten, in dem ich an so einen ECHO-Server ein Paket sende und die Absenderadresse (IP) fälschen.

Durchsatz

Bleibt zuletzt noch die Frage, wie schnell ein PowerShell-Script UDP-Pakete senden kann. Dazu muss man aber bedenken, dass UDP kein gesichertes Protokoll wie TCP ist. Es gibt keine "Connections" und verlorene Pakete werden nicht vom IP-Stack erneut gesendet. für Voice und Video ist dies ideal ebenso wie für Meldungen wie SNMP und SYSLOG, die nicht jedes mal einen TCP-Handshake durchführen wollen. Insofern verbietet sich schon die Übertragung größerer Datenmengen da sie ansonsten selbst den Mehrwert von TCP selbst implementieren müssten.

Dennoch ist es natürlich schon interessant, wie schnell ein PC mit PowerShell UDP-Pakete zu einem anderen PC übertragen kann und so habe ich den UDP-Sender einmal mit 100byte großen Pakete, 0ms Verzögerung auf eine nicht vorhanden IP-Adresse in meinem VirtualBox-Subnetz gestartet und mit dem Ressource-Monitor geschaut:

Sicher kann man keine Wunder von einem "Skript" erwarten, aber damit schon 4,4MByte/Sek oder ca. 44MBit zu erzeugen ist zumindest nicht zu verachten. Ein Netzwerk wird man damit aber nicht fluten können. Ich habe den Test noch mal mit 1420 Bytes wiederholt und einfach auf den "DISCARD"-Port eines Systems gesendet.

param (
   [string]$remoteip = "fritz.box", # IP to send to 
   [int]$remoteudpport=9,           # port to send to
   [int]$sourceudpport = 0          # SourcePort, maybe empty uses and available port
)

Write-host "Init UDP-Socket"
$udpClient = new-Object system.Net.Sockets.Udpclient($sourceudpport)
Write-host "Init DataBuffer"
$byteBuffer  = [System.Byte[]]::new(1460)

$totalbytes=0
$totalpakets=0
$stopwatch =  [system.diagnostics.stopwatch]::New()
$stopwatch.start()
Write-host "Start Loop"
while (!([console]::KeyAvailable)) {
   $sendbytes = $udpClient.Send($byteBuffer, $byteBuffer.length, $remoteip, $remoteudpport)
   if ($sendbytes -ne $byteBuffer.length) {
     write-host "Problem sending" -forgroundcolor yellow
   } 
   else {
      $totalbytes+=$bytebuffer.count
      $totalpakets++
   }
   if ($stopwatch.elapsed.TotalMilliseconds -gt 1000) {
      Write-host "TotalBytes $($totalbytes)/Sec   Total Pakets $($totalpakets)"
      $totalbytes=0
      $totalpakets=0
      $stopwatch.restart()
   }
}
[system.console]::readkey($true) | out-null

Das Ergebnis war auf meinem Lenovo T480s (Core i7-8550) ernüchternd: Mehr als 40-50 Megabit sind so selbst über ein Gigabit-Link nicht möglich.

PS C:\temp> .\udpstorm.ps1
Init UDP-Socket
Init DataBuffer
Start Loop
TotalBytes 4250060/Sec   Total Pakets 2911
TotalBytes 4277800/Sec   Total Pakets 2930
TotalBytes 4509940/Sec   Total Pakets 3089
TotalBytes 4330360/Sec   Total Pakets 2966
TotalBytes 4355180/Sec   Total Pakets 2983
TotalBytes 4362480/Sec   Total Pakets 2988
TotalBytes 4308460/Sec   Total Pakets 2951
TotalBytes 4766900/Sec   Total Pakets 3265
TotalBytes 4870560/Sec   Total Pakets 3336

Ich kann also nicht wirklich sagen, dass PowerShell die UDP-Schnittstelle auslasten könnte und die Messung ist dahingehend also auch nur bedingt aussagekräftig. Die gleichen Test macht Fritz!App WLAN App auf IOS und Android. Die Ergebnisse dort sind auch mit Vorsicht zu betrachten.

Ich bin gespannt, ob Messungen auf einer potenteren Plattform mit einem echten LAN andere Werte bringen.

UDP und Multicast

Die verbindungslose Übertragung bietet sich natürlich auch dazu an, Pakete an viele Gegenstellen zu senden. Dazu eigenen sich insbesondere "Multicast"-Adressen. Sie können damit selbst schnell mal Daten in einem LAN verteilen. das kann z.B. im IoT-Bereich interessant sein, wenn ein Sensor seine Daten nicht an genau eine Ziel-Adresse sondern eine Multicast-Adresse sendet. Solange die Datenmenge überschaubar ist, gibt es da nichts dagegen zu sagen.

Sehr viele Protokolle nutzen heute schon Multicast. Dazu zählen z.B. SSDP, MediaServer, VoIP-Telefone aber auch MulticastDNS (mDNS). Wer eine Fritz!Box im LAN hat, kann auch deren SSDP-Pakete finden

Entsprechend interessant kann es sein, per PowerShell auch Multicast-Pakete zu empfangen oder zu senden. Allerdings ist das nicht ganz so einfach, da Sie sich für die Multicast-Adresse registrieren müssen. Für eine Fritz!Box ist das dann:

$udpClient = New-Object System.Net.Sockets.Udpclient  #without IP address to set additional options
$udpclient.Client.ExclusiveAddressUse = $false
$udpclient.Client.MulticastLoopback = $true
$udpclient.Client.SetSocketOption([System.Net.Sockets.SocketOptionLevel]::Socket, [System.Net.Sockets.SocketOptionName]::ReuseAddress, $true)
$LocalIpEndPoint = New-Object System.Net.IPEndPoint([system.net.ipaddress]::any, 1900);
$udpClient.Client.Bind($LocalIpEndPoint) 
$RemoteIpEndPoint = New-Object System.Net.IPEndPoint([system.net.ipaddress]::any , 0);
$udpClient.JoinMulticastGroup([system.net.ipaddress]::Parse("235.255.255.250"))

Write-host "Receive-UDP:Wait for Data on Port: $listenport"
while ($true) {
write-host "Waiting for Packets"
   # wait for data arriving from any ip. Alternativ kann man eine Liste der erlaubten IPs angeben
   $data= $udpclient.receive([ref]$RemoteIpEndPoint)
   $receivetimestamp = get-date -format o
   write-host "Paket received at $($receivetimestamp)"
   Write-host "Sender: $($RemoteIpEndPoint.tostring())"
   [string[]]$payload =([system.text.encoding]::ASCII.GetString($data))  -split "`r`n"
   $payload
}

Wenn ihr Adapter dann mitspielt, sollten Sie alle Multicast-Pakete auf die Adresse "235255.255.250" und Port 1900 sehen.

Weitere Links