Leider ist die Dokumentation zum UdpClient unter .NET oder DatagramSocket unter Java nicht immer vollständig, aussagekräftig und korrekt. Die Möglichkeiten sind vielfältig und es gibt viele Spezialfälle. Die grundlegenden Funktionen werden hier erläutert und wurden auf einem PC mit Windows 10 mit .Net Framework 4.7.2 bzw. Java Version 8 Update 181 überprüft. Der Schwerpunkt liegt dabei auf der UdpClient-Klasse von .NET. Die DatagramSocket von Java funktioniert in etwa ähnlich. Hierauf wird nur kurz eingegangen und auf die entsprechende Funktionalität der UdpClient-Klasse verwiesen.
Die folgenden (Code)-Details beziehen sich, wenn nicht anders angegeben, auf IP-Adressen im Format IPv4. Ob das zu Grunde liegende Betriebssystem IPv6-Adressen unterstützt, kann mit der folgenden Anweisung abgefragt werden.
If Socket.OSSupportsIPv6 Then ...
Nahezu alle aktuellen Betriebssysteme unterstützen die IPv6-Adressierung.
Die UdpClient-Klasse verwaltet ein internes Socket-Objekt und nutzt dieses zur Kommunikation. Viele der gemachten Angaben beruhen demzufolge auf dem Verhalten der Socket-Klasse und gelten deshalb auch für andere .Net-Klassen, die ebenfalls ihre Methoden über ein Socket-Objekt abwickeln.
Ein kleines Testprogramm mit dem UDP-Datagramme versandt und empfangen werden können, steht als VS2017-Projekt im Source-Code als auch als Binary zur Verfügung.
Inhaltsverzeichnis
Es geht um Begriffe wie Unicast, Broadcast und Multicast bzw. um die zugehörigen IP-Adressen. Dabei handelt es sich um Adressierungsschemata der Vermittlungsschicht im OSI-Modell. Der Wikipedia-Artikel zu diesen Themen bieten zahlreiche weitergehende Informationen. Die folgenden Grafiken sind ebenfalls diesen Artikeln entnommen.
Bei der Unicast-Adressierung werden die Daten genau an einen Empfänger geschickt. Alle IP-Adressen, die keine der folgenden speziellen Adressen sind, sind Unicast-Adressen.
Eine Broadcast-Nachricht wird an alle Mitglieder eines Netzwerks geschickt. Den Versand einer Broadcast-Nachricht bewirkt man allen durch die Angabe einer Broadcast-Adresse als Empfänger-IP. Man unterscheidet zwischen Limited Broadcast und Directed Broadcast.
Als Ziel wird die IP-Adresse 255.255.255.255 angegeben. Damit werden alle Mitglieder im eigenen lokalen Netz angesprochen. Ein Limited Broadcast wird von einem Router nicht weitergeleitet, verlässt also das lokale Netzwerk nicht.
Das Ziel sind alle Teilnehmer eines ausgewählten Netzes. Die Adresse wird durch die Kombination aus Zielnetz und dem Setzen aller Hostbits auf 1 angegeben. Die Adresse für einen Directed Broadcast in das Netz 192.168.0.xxx mit der Netzmaske 255.255.255.0 lautet somit: 192.168.0.255. Ein Directed Broadcast wird von einem Router weitergeleitet, falls Quell- und Zielnetz unterschiedlich sind. Falls Quell- und Zielnetz identisch sind, entspricht dies einem Limited Broadcast (weitere Spezialbegriffe unter Wikipedia IP-Broadcasts).
Multicast ist etwas komplexer. Hier werden Nachrichten zeitgleich an eine geschlossene Teilnehmergruppe übertragen. In IPv4 ist hierfür der Adress-Bereich 224.0.0.0 bis 239.255.255.255 reserviert. Die Verteilung der Nachrichten erfolgt durch den Router, Switch oder Hub. Damit dieser weiß, an wen eine Multicast-Nachricht weitergeleitet werden muss, müssen sich die Teilnehmer beim Netzwerk als zur Gruppe gehörig anmelden. Hierzu stehen i.d.R. implizite oder explizite Methoden zur Verfügung. Unter z.B. .Net sind dies die Methoden JoinMulticastGroup und DropMulticastGroup.
Zum Versenden eines Datagrams sind mindestens drei Angaben notwendig. Dies sind:
Der Endpunkt, der die Nachricht empfangen soll, heißt in der .NET-Umgebung Remote-Endpunkt (remote endpoint). Das Gerät, das diesen Endpunkt verwaltet, heißt dementsprechend Remote-Host. In der Regel ist dies ein Netzwerk-Interface (NIC), z.B. eine Netzwerk-Karte oder ein WLAN-Stick.
Für host und remote gibt es keine passenden deutschen IT-Begriffe. Hier werden üblicherweise die englischen Bezeichnungen verwandt.
Der Endpunkt, der zur Kommunikation auf dem eigenen Gerät (in der eigenen Anwendung) dient, heißt lokaler Endpunkt (local endpoint). Das zugehörige Gerät bzw. NIC wird als lokaler Host (local host) bezeichnet.
Ein IP-Endpunkt besteht aus einer IP-Adresse, die einem Netzwerk-Interface (NIC, Host) zugeordnet ist, und einer Port-Nummer. Eine zusätzliche Eigenschaft ist die Familienzugehörigkeit (AddressFamily). Diese kann den Wert InterNetwork (2) haben, dann handelt es sich im eine IPv4-Adresse, oder InterNetworkV6 (23) für eine IPv6-Adresse. Ein NIC kann meist sowohl über IPv4 als auch über IPv6 kommunizieren.
I.d.R. besitzen die meisten Endgeräte mehrere NICs. Diese können z.T. auch virtuell sein. Jeder PC besitzt z.B. das virtuelle Interface mit der IP-Adresse 127.0.0.1 (local host, loopback), mit dem Daten an das eigene Gerät versandt werden können.
Eine Übersicht über die vorhandenen Netzwerk-Interfaces erhält man unter Windows mit dem Kommando
netsh interface ip show config
bzw. in .NET über
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
Neben den oben genannten IP-Adressen gibt es in .NET noch eine unspezifizierte Adresse mit der Bezeichnung Any (0.0.0.0) bzw. IPv6Any (::). Diese kann verwandt werden, wenn keine keine bestimmte IP-Adresse angegeben werden muss.
Zum Austausch von Datagrammen müssen nicht immer alle Adressangeben von der Applikation bereit gestellt werden. Z.T. kann man dies auch dem zu Grunde liegenden Dienstanbieter überlassen. In diesem Fall wird die Adresse Any bzw. IPv6Any und die Port-Nummer 0 verwandt.
Der lokale Endpunkt wird sowohl zum Senden als auch zum Empfangen von Datagrammen benutzt. Dabei kann man entscheiden, in wie weit der Endpunkt durch die Anwendung spezifiziert wird oder dem Dienstanbieter die Auswahl der Adressdaten überlassen wird. Lediglich die Adress-Familie muss implizit (Voreinstellung ist IPv4) oder explizit festgelegt werden. Sowohl di IP-Adresse kann unbestimmt bleiben (Any), als auch der Port (Angabe 0).
Der lokale Endpunkt wird über den Konstruktor der UdpClient-Klasse oder nachträglich über UdpClient.Client.Bind festgelegt (die Eigenschaft UdpClient.Client liefert das zu Grunde liegende Socket-Objekt zurück).
Konstruktor | Beschreibung | Senden | Empfangen |
---|---|---|---|
New UdpClient() | Der lokale Endpunkt wird nicht festgelegt. Dieser Konstruktor erstellt einen neuen UdpClient und ermöglicht dem zugrunde liegenden Dienstanbieter, die am besten geeignete lokale IPv4-Adresse und Portnummer auszuwählen. Wenn dieser Konstruktor verwendet wird, wird für die UdpClient-Instanz eine Adressfamilie vom Typ IPv4 festgelegt, die nicht geändert oder durch einen Aufruf einer Verbindungsmethode mit einem IPv6-Ziel überschrieben werden kann. |
Bei jedem Sende-Vorgang wählt der Dienstanbieter einen passenden Endpunkt aus. Die IP-Adresse (das NIC) wird so gewählt, dass die Zieladresse erreichbar ist. Für den Port wird ein beliebiger freier Port gewählt. | Der Empfang von Datagrammen ist nicht möglich. |
New UdpClient (Family As AddressFamily) | Der lokale Endpunkt wird nicht festgelegt. Als Angaben kann InterNetwork oder InterNetworkV6 gemacht werden. Hierdurch ein ein UdpClient für IPv6 angelegt werden. | wie oben | Der Empfang von Datagrammen ist nicht möglich. |
New UdpClient (Port As Integer) | Initialisiert eine neue Instanz der UdpClient-Klasse
und bindet sie an die angegebene lokale Portnummer. Die zugehörige IP-Adresse ist Any. Wenn die Portnummer 0 an den Konstruktor übergeben wird, weist der zugrunde liegende Dienstanbieter eine (zufällige) Portnummer zu. Diese kann über UdpClient.Client.LocalEndPoint abgefragt werden. |
wie oben | Empfangen werden Datagramme an diesen Port über alle vorhandenen NICs. |
New UdpClient (Port As Integer, Family As AddressFamily) | Wie oben, aber mit der Möglichkeit zu IPv6-Adressen zu wechseln. | wir oben | wie oben |
New UdpClient (LocalEP As IPEndPoint) | Der Client wird an den angegebenen Endpunkt gebunden. Die AdressFamily
der IP-Adresse bestimmt die Adress-Familie des UdpClient. Die IP-Adresse muss entweder Any oder die eines vorhandenen NICs sein. Wenn die die enthaltene Portnummer 0 ist, weist der zugrunde liegende Dienstanbieter eine (zufällige) Portnummer zu. Diese kann über UdpClient.Client.LocalEndPoint abgefragt werden. |
IP ≠ Any: Die
angegebene IP wird als Absender benutzt und über das betreffende NIC versandt. IP = Any: Bei jedem Sende-Vorgang legt der Dienstanbieter einen passenden Endpunkt aus. Die IP-Adresse (das NIC) wird so gewählt, dass die Zieladresse erreichbar ist. |
IP ≠ Any: Datagramme
werden über das zugehörige NIC empfangen, wenn sie an den angegeben Port (ggf.
den Zufall-Port) gesendet werden. IP = Any: Datagramme werden über alle NICs empfangen, wenn sie an den angegeben Port (ggf. den Zufall-Port) gesendet werden. |
New UdpClient (Hostname As String, Port As Integer) | Dieser Konstruktor legt den (Standard-)
Remote-Endpunkt (!) fest (s.u.). Der Hostname kann eine IP-Adresse sein, z.B. "192.168.1.1". Die Adresse ist beliebig. Es sind auch IPv6-Adressen erlaubt. Die Adresse muss nicht erreichbar sein. Any (0.0.0.0) ist nicht erlaubt. Es wird das NIC bzw. dessen IP-Adresse als lokaler Endpunkt gewählt, über das die Remote-Adresse erreichbar ist. Es kann aber auch ein "echter" Name angegeben werden, wie z.B. "microsoft.com". In diesem Fall muss der Host erreichbar sein, weil dessen IP-Adresse über eine DNS-Abfrage ermittelt wird. Es wird das NIC bzw. dessen IP-Adresse als lokaler Endpunkt gewählt, über das die Remote-Adresse erreichbar ist. Die vergebene Port-Nummer des lokalen Endpunkts ist immer zufällig. |
Die Absender-Adresse ist die des vom Dienstanbieter ausgewählten NICs. Die Port-Nummer wird zufällig gewählt. | Daten können nur vom festgelegten Remote-Endpunkt über die Zufalls-Port-Nummer empfangen werden. |
Der Remote-Endpunkt kann auf zwei Arten festgelegt werden, entweder als Standard-Zielgerät (Standard-Remote-Host) oder beim Senden von Datagrammen auch dynamisch als Adress-Angabe in der Methode send.
Beschreibung | Senden | Empfangen |
---|---|---|
Es ist kein Standard-Remote-Host festgelegt. | Die Adresse des Remote-Host muss entweder als IPEndpoint
oder als Host-Name gemeinsam mit einer Port-Angabe übergeben werden: Send (Datagram As Byte(), BytesNum As Integer, EndPoint As IPEndPoint) bzw. Send (Datagram As Byte(), BytesNum As Integer, HostName As String, RemotePort As Integer) Beide Methoden werfen eine Exception, wenn ein Standard-Remote-Host festgelegt wurde. |
Es können Datagramme von jedem beliebigen Absender empfangen werden. |
Ein Standard-Remote-Host ist festgelegt. | Es darf keine Empfänger-Adresse angegeben werden: Send (Datagram As Byte(), BytesNum As Integer) Diese Methode wirft eine Exception, wenn kein Standard-Remote-Host festgelegt wurde |
Datagramme können nur von dem angegeben Endpunkt empfangen werden. Sowohl die IP-Adresse als auch der Port des Absenders müssen mit denen des Standard-Remote-Host übereinstimmen. |
Das Festlegen eines Remote-Host geschieht entweder schon bei der Anlage der UdpClient-Instanz über den Konstruktor New UdpClient (Hostname As String, Port As Integer) (s.o.) oder über die Methode connect. Hier stehen wieder die üblichen Varianten zur Verfügung:
Die UdpClient-Klasse besitzt keine Methode, den Standard-Remote-Host zu ändern oder zu löschen. Die Dokumentation besagt zwar
If you want to send datagrams to a different remote host, you must make another call to the Connect method ...
Dies funktioniert aber nicht. Wenn zu einem anderen Standard-Remote-Host gewechselt werden soll, muss eine neue Instanz der UdpClient-Klasse erstellt werden.
Die Methode send dient zum Versenden von Datagrammen. Die Festlegung des lokalen Endpunkts, über den das Datagramm versandt wird, geschieht über den Konstruktor der UdpClient-Klasse bzw. über die Methode UdpClient.Cleint.Bind. Dies wurde bereits oben beschrieben. Neben der Methode send gibt es noch die asynchronen Varianten BeginSend und SendAsync. Das Versenden eines UDP-Datagramms erfordert normalerweise nur wenig Zeit. Lediglich, wenn ein Host-Name verwendet wird, der erst über eine DNS-Anfrage aufgelöst werden muss, können je nach DNS-Server leichte Verzögerungen auftreten.
Die einfachste Art, ein Datagramm zu versenden, ist:
Sub UdpXmit(Data As Byte(), RemoteEndpoint As IPEndPoint)
Dim a = New UdpClient().Send(Data, Data.Length, RemoteEndpoint)
End Sub
Zum Empfangen von Datagrammen steht die Methode Receive (ByRef remoteEP As IPEndPoint) As Byte() zur Verfügung. Sie liefert als Rückgabewert die Nutzdaten (Payload) eines empfangenen Datagramms. Die per Referenz übergebene Variable remoteEP wird mit den Adress-Daten des Absenders (Remote-Host) gefüllt. Es werden immer nur die Daten eines einzigen Datagramms zurück geliefert.
Die Methode blockiert, bis entweder ein Datagramm empfangen wurde oder die Zeitbeschränkung (Timeout) abgelaufen ist. Das Timeout kann über die Eigenschaft UdpClient.Client.ReceiveTimeout festgelegt werden. Die Angabe erfolgt in Millisekunden. Der Standardwert ist 0 und gibt ein unendliches Timeoutintervall an. Durch die Angabe von -1 wird ebenfalls ein unendliches Timeoutintervall angegeben. Ist das Timeout abgelaufen, wird eine SocketException geworfen.
Über die Eigenschaft UdpClient.Available kann ermittelt werden, wie viele Datenbytes abgerufen werden können. Dies ist die Summe aller gepufferten Datagramme. Receive ruft aber immer nur ein einzelnes Datagramm ab.
Ein einfacher Datenaustausch zwischen zwei Geräten (Ping) ließe sich wie folgt realisieren:
Function UdpXmitReceive(Data As Byte(), RemoteHost As String) As Byte()
Dim u = New UdpClient(RemoteHost, 0) ' Client mit beliebigen lokalen Endpunkt
' mit Remote-Endpunkt verbinden.
' Kann RemoteHost nicht aufgelöst werden,
' wird eine SocketException geworfen
u.Send(Data, Data.Length) ' Daten senden
u.Client.ReceiveTimeout = 500 ' Empfangs-Timeout festlegen
Dim RemoteEP As New IPEndPoint(IPAddress.Any, 0) ' Leeren Endpunkt für die Remote-Adresse anlegen
Dim RcvData(-1) As Byte ' Feld für Empfangsdaten anlegen
Try
RcvData = u.Receive(RemoteEP) ' Auf Empfang eines Datagramms warten
Catch ex As SocketException ' Timeout
End Try
Return RcvData ' Daten zurück geben
End Function
Dem UdpClient in .NET entspricht der DatagramSocket in Java (s. DatagramSocket). Die Funktionsweise ist sehr ähnlich.
Zum Testen ist hier eine UDPServer- und eine UDPClient-Applikation erläutert: https://www.pegaxchange.com/2018/01/23/simple-udp-server-and-client-socket-java/
Das binden an einen lokalen Endpunkt erfolgt bereits über den Konstruktor.
Konstruktor | Beschreibung | Senden | Empfangen |
---|---|---|---|
DatagramSocket() | Erstellt einen Datagramm-Socket und bindet ihn an einen beliebigen verfügbaren Port auf
dem lokalen Host-Computer. Die IP-Adresse ist 0.0.0.0. Der vergebene Port kann über DatagramSocket.getLocalPort() abgerufen werden. |
Bei jedem Sende-Vorgang wählt der Dienstanbieter einen passenden Endpunkt aus. Die IP-Adresse (das NIC) wird so gewählt, dass die Zieladresse erreichbar ist. | Empfangen werden Datagramme an den zufällig vergeben Port über alle vorhandenen NICs. |
DatagramSocket (int port) | Erstellt einen Datagramm-Socket und bindet ihn an den angegebenen Port auf dem lokalen Host-Computer. Die IP-Adresse ist 0.0.0.0. | Wie oben | Empfangen werden Datagramme an den angegebenen Port über alle vorhandenen NICs. |
DatagramSocket (int port, InetAddress laddr) | Erstellt einen Datagramm-Socket, der an die angegebene lokale Adresse gebunden ist. Die IP-Adresse muss die eines NIC sein. Eine ungültige IP führt zu einer SocketException (BindException). | Die Absender-Adresse ist der angegebene Endpunkt. | Empfangen werden Datagramme die an die angegebene IP und den angegebenen Port versandt werden. |
DatagramSocket (SocketAddress bindaddr) | Erstellt einen Datagramm-Socket, der an die angegebene lokale Socket-Adresse gebunden ist.
Als Socket-Adresse steht z.Zt. (Java SE 8) die Klasse InetSocketAddress
zur Verfügung. SocketAddress entspricht in etwa der
IpEndPoint-Klasse in .NET. Ist die Port-Angabe in bindaddr 0, wird ein beliebiger verfügbarer Port ausgewählt. Die Angabe 0.0.0.0 für die IP-Adresse ist erlaubt. Wenn die Adresse null ist, wird ein ungebundener Socket erstellt, d.h. die lokale IP-Adresse ist 0.0.0.0 und der lokale Port ist 0. |
Regeln wie oben, je nach Ausprägung der in bindaddr enthalten IP-Adresse und enthaltener Port-Nummer. | Regeln wie oben, je nach Ausprägung der in bindaddr enthalten IP-Adresse und enthaltener Port-Nummer. |
Alle dies Konstruktoren erstellen ein gebundenes DatagramSocket-Objekt. Das erneute Binden an eine andere Adresse über die Methode bind funktioniert nicht (SocketException).
Auch Java kennt einen Standard-Remote-Endpunkt. DatagramSocket.connect verbindet den Socket mit einer Remote-Adresse für diesen Socket. Wenn ein Socket mit einer Remote-Adresse verbunden ist, können Pakete nur an diese Adresse gesendet und nur von dieser empfangen werden. Standardmäßig ist ein Datagramm-Socket nicht verbunden.
Im Gegensatz zum erneuten Binden kann das DatagramSocket-Objekt erneut mit einer anderen Adresse verbunden werden (reconnect). Das Verbinden mit (0.0.0.0:0) hebt die Verbindung nicht auf. Dies geschieht erst mit DatagramSocket.disconnect.
Zum Senden oder zum Empfangen benötigt man ein DatagramPacket-Objekt. Dieses enthält sowohl die Nutzdaten, als auch die Adress-Daten des Remote-Endpunkts.
Beim Empfangen werden die Adress-Komponenten des Pakets mit den Adressdaten des Absenders gefüllt (Remote-Host).
Vor dem Senden müssen sie mit den Adressdaten des Ziels (Remote-Host) gefüllt werden. Ist ein Standard-Remote-Endpunkt festgelegt müssen die Adressdaten des Pakets mit denen des Standard-Remote-Endpunkts übereinstimmen.
Source-Code" | Binary |