UDP Broadcasts
User Datagram Protocol (UDP)
UDP ist ein Netzwerkprotokoll der Transportschicht (wie TCP).
Vergleich mit TCP:
TCP |
UDP |
verbindungsorientiert (Drei-Wege-Handshake) |
verbindungslos |
zuverlässig (Übertragung der Pakete in richtiger Reihenfolge wird gesichert) |
nicht zuverlässig (Pakete können verloren gehen, doppelt oder unsortiert ankommen) |
Pakete werden ggf. neu gesendet |
Pakete werden nicht neu gesendet |
Vergleichsweise großer Header |
Vergleichsweise kleiner Header |
Anwendungsgebiete:
- Einfache Frage-Antwort-Protokolle (z.B. DNS), da Netzwerkbelastung mit UDP geringer ist
- Übertragungen, die bei hoher Netzwerkauslastung eher geringere Qualität haben sollen als langsamer zu werden (z.B. Voice over IP)
UDP-Broadcasts
UDP-Broadcasts können verwendet werden, damit sich Client und Server in einem Subnetz finden, ohne zunächst zu wissen, auf welchen Hosts sie laufen. Der Server öffnet dazu einen Socket, der auf einem bestimmten Port lauscht. Der Client kann dann einen UDP-Broadcast auf diesen Port an alle Hosts im Subnetz schicken, worauf der Server antworten kann.
Ein einfacher UDP-Server, der auf alle Anfragen den String "hello" zurückschickt:
1 from SocketServer import UDPServer, BaseRequestHandler
2
3 class Handler(BaseRequestHandler):
4 def handle(self):
5 print "message:", self.request[0]
6 print "from:", self.client_address
7 socket = self.request[1]
8 socket.sendto("hello", self.client_address)
9
10
11 addr = ("", 5555)
12 print "listening on %s:%s" % addr
13 server = UDPServer(addr, Handler)
14 server.serve_forever()
Wichtig ist, dass der Socket an den leeren String gebunden wird, das entspricht INADDR_ANY und bedeutet, dass von beliebigen Hosts empfangen wird. (Anmerkung: Funktioniert so nur bei IPv4).
Der Client broadcastet dann wie folgt, in diesem Beispiel sendet er ebenfalls den String "hello" an den Server:
1 import socket
2
3 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
4 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
5 s.settimeout(5)
6
7 s.sendto("hello", ("<broadcast>", 5555))
8 try:
9 print "Response: %s" % s.recv(1024)
10 except socket.timeout:
11 print "No server found"
12
13 s.close()
Hier ist wichtig, an <broadcasts>, also INADDR_BROADCAST zu senden. (Anmerkung: Funktioniert so nur bei IPv4). Man kann die Broadcastadresse auch manuell setzen.
Über die UDP-Verbindung können die beiden Komponenten z.B. ihre Hostnamen austauschen, um dann eine TCP-Verbindung aufzubauen.
Beispiel: Programm, das autmatisch zu Server oder Client wird
Hier ist ein Beispiel für ein Programm, dass beim Start versucht, Kontakt zu einem schon vorhandenen Server aufzunehmen. Kommt kein solcher Kontakt zu stande, wird das Programm selbst zum Server, andernfalls wird es zum Client. Über die UDP-Nachrichten werden weitere Informationen ausgetauscht und geprüft, ob wir auch mit "unserem" Server/Client sprechen und nicht mit Programmen, die zufällig den gleichen Port benutzen.
1 import socket
2 import threading
3 from SocketServer import UDPServer, BaseRequestHandler
4
5 PORT = 55555
6
7
8 class MasterUDPServer(threading.Thread):
9 def run(self):
10 addr = ("", PORT)
11 print "UDP server listening on", addr
12 server = UDPServer(addr, Handler)
13 server.serve_forever()
14
15
16 class Handler(BaseRequestHandler):
17
18 number_client = 0
19 my_addr = socket.getfqdn()
20
21 def handle(self):
22 request = self.request[0].strip()
23 if request == "TEST": # is it our UDP client?
24 Handler.number_client += 1
25 print "Detected %i. client on %s" % (Handler.number_client,
26 self.client_address)
27 socket = self.request[1]
28 reply = "TEST %i %s" % (Handler.number_client, Handler.my_addr)
29 socket.sendto(reply, self.client_address)
30
31
32 if __name__ == "__main__":
33 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
34 s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
35 s.settimeout(3)
36
37 print "Trying to find server..."
38 s.sendto("TEST", ("<broadcast>", PORT))
39 try:
40 response = s.recv(1024).strip()
41 if not response.startswith("TEST"):
42 raise ValueError() # it's not our UDP server
43
44 print "Found server."
45 (dummy, my_number, server_addr) = response.split()
46 print "I am client number", my_number
47 print "Server is on", server_addr
48
49 except socket.timeout, ValueError:
50 print "Could not find server, starting new server."
51 udpserver = MasterUDPServer()
52 udpserver.start()
53
54 s.close()
Die Ausgabe beim ersten Start des Programmes sieht so aus:
$ ./udp_test.py
Trying to find server...
Could not find server, starting new server.
UDP server listening on ('', 55555)
Detected 1. client on ('nnn.nnn.168.44', 55896)
Detected 2. client on ('nnn.nnn.168.23', 32889)Startet man das Programm nochmals, erhält man:
$ ./udp_test.py Trying to find server... Found server. I am client number 1 Server is on somehost
$ ./udp_test.py Trying to find server... Found server. I am client number 2 Server is on somehost
Weitere Möglichkeiten von UDP
UDP-hole-punching, um peer-to-peer-Verbindungen trotz Firewall zu ermöglichen.
macro::