Benutzer mit den meisten Antworten
UDP Telegramme unter Windows

Frage
-
Hallo Forum
Ich hab mittlerweile schon einige Testprogramme geschrieben, die UDP-Telegramme versenden und auch empfangen, aber irgendwie hab ich dann immer wieder die Situation, dass ich mir nicht sicher bin, ob ich das Ganze überhaupt verstanden hab (weil's nämlich partout nicht funktionieren will).
Daher an dieser Stelle ein paar Grundsatzfragen:
Wenn ich ein Programm habe, das grundsätzlich UDP-Telegramme empfängt, dann muss ich doch wohl folgendermaßen initialisieren:
SOCKET WS; SOCKADDR_IN in_addr; if ((WS = socket(AF_INET,SOCK_DGRAM,0)) != INVALID_SOCKET) { in_addr.sin_family = AF_INET; // IP in_addr.sin_port = htons(port);// port in_addr.sin_addr.S_un.S_addr= ADDR_ANY; // jeder darf sich mit uns verbinden if (bind(WS,(sockaddr*)&in_addr,sizeof(in_addr)) != SOCKET_ERROR) { // binden comopen = true; } }
Auf diese Weise sag ich dem OS, dass ich gerne einen Socket hätte, der zugleich dann auch noch auf Port Nr. port hören soll, ob da nicht was komme.
Was aber, wenn ich hier nichts bekomme und daher den Sender (den ich in meinem Fall noch nicht kenne) über ein Broadcast-Telegramm auffordern will, mir doch was zu schicken. Ist es dann irrelevant, dass mein Socket schon an eine Port-Nummer gebunden ist oder nicht? Oder andersherum gefragt, kann ich trotz obiger Initialisierung dann einen sendto gemäß
SOCKADDR_IN out_addr; out_addr.sin_family = AF_INET; out_addr.sin_port = htons(port); out_addr.sin_addr.S_un.S_addr = 0xFFFFFFFF; // Broadcast... sendto(WS,"hallo!",6,0,(SOCKADDR*)outaddr,sizeof(outaddr));
(Die MSDN schweigt sich meiner Meinung nach etwas über die Flags bei sendto aus)
machen, oder fehlt da noch was...oder brauch ich dafür einen anderen Socket?
Grüße
FireHeart
Antworten
-
Hallo FireHeart,
Ich denke einige Probleme entdeckt zu haben.
Zum einen solltest Du beim Erzeugen des Sockets UDP auswählen indem Du als dritten Parameter IPPROTO_UDP angibst:
UdpSocket::UdpSocket() { // Socket erzeugen socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (socket == INVALID_SOCKET) throw SocketException(__FUNCTION__, "socket", ::WSAGetLastError()); }
Des weiteren mußt Du einen UDP Socket erst für Broadcasts freischalten. Ich verwende dazu folgenden Code
void UdpSocket::enableBroadcast(long port) { BOOL value = TRUE; if (::setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*) &value, sizeof(value)) == SOCKET_ERROR) throw SocketException(__FUNCTION__, "setsockopt(SO_BROADCAST)", ::WSAGetLastError()); memset(&broadcastAddr, 0, sizeof(broadcastAddr)); broadcastAddr.sin_family = AF_INET; broadcastAddr.sin_port = htons( (u_short)port ); broadcastAddr.sin_addr.S_un.S_addr = htonl(INADDR_BROADCAST); }
Ein Broadcast wird natürlich schon an einen speziellen Port gesendet und somit muß auch bei der Broadcastadresse ein gültiger Port angegeben sein. Der Port des Absenders ist dabei nicht relevant.
255.255.255.255 (bzw. INADDR_BROADCAST) kannst Du sehr wohl für Broadcasts im lokalen Subnet verwenden, da Router üblicherweise nicht konfiguriert sind Broadcasts weiterzuleiten und Dein Broadcast Dein Subnet deshalb nicht verlassen wird.
Den Empfangssocket binde ich übrigens so:
void UdpSocket::bind(unsigned short port) { // Binden memset(&localAddr, 0, sizeof(localAddr)); localAddr.sin_family = AF_INET; localAddr.sin_port = htons( (u_short) port ); localAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); if (::bind(socket, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) throw SocketException(__FUNCTION__, "bind", ::WSAGetLastError()); }
(wobei localAddr ein Member vom Typ SOCKADDR_IN ist)
Noch ein Tip: Bei UDP gibts gegenüber TCP keine Rückbestätigung und auch kein "Puffer voll", d.h. wenn der Empfänger keine Daten annimmt (weil sein Puffer voll ist) dann werden die UDP Pakete still und heimlich verworfen. Der Empfangspuffer ist unter Winsock 8kb groß, kann aber über ::setsockopt(s, SOL_SOCKET, SO_RCVBUF, ...) eingestellt werden.
mfg
Andreas
- Bearbeitet Andreas Hammerschmidt Montag, 15. September 2014 05:14 typo
- Als Antwort markiert Fire-Heart Montag, 15. September 2014 07:55
Alle Antworten
-
Ich GLAUBE das sollte egal sein (hab sowas aber noch nicht implementiert). Broadcasting geht nur an alle IP-Adressen - das port sollte eigentlich völlig transparent dazu sein. Liegt es vllt. an der Netzmaske? Die broadcast-Adresse für das Netz 192.168.0.0/24 ist zB 192.168.0.255 und nicht 255.255.255.255. Alles FF wäre uU ein internet-weiter broadcast...
--Das oben ist glaube ich Quatsch. Aber mal hier lesen. Man muss für broadcasting noch eine socket-option setzen.
- Bearbeitet Heiko Lewin Dienstag, 5. August 2014 14:20
-
Hallo FireHeart,
Ich denke einige Probleme entdeckt zu haben.
Zum einen solltest Du beim Erzeugen des Sockets UDP auswählen indem Du als dritten Parameter IPPROTO_UDP angibst:
UdpSocket::UdpSocket() { // Socket erzeugen socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (socket == INVALID_SOCKET) throw SocketException(__FUNCTION__, "socket", ::WSAGetLastError()); }
Des weiteren mußt Du einen UDP Socket erst für Broadcasts freischalten. Ich verwende dazu folgenden Code
void UdpSocket::enableBroadcast(long port) { BOOL value = TRUE; if (::setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*) &value, sizeof(value)) == SOCKET_ERROR) throw SocketException(__FUNCTION__, "setsockopt(SO_BROADCAST)", ::WSAGetLastError()); memset(&broadcastAddr, 0, sizeof(broadcastAddr)); broadcastAddr.sin_family = AF_INET; broadcastAddr.sin_port = htons( (u_short)port ); broadcastAddr.sin_addr.S_un.S_addr = htonl(INADDR_BROADCAST); }
Ein Broadcast wird natürlich schon an einen speziellen Port gesendet und somit muß auch bei der Broadcastadresse ein gültiger Port angegeben sein. Der Port des Absenders ist dabei nicht relevant.
255.255.255.255 (bzw. INADDR_BROADCAST) kannst Du sehr wohl für Broadcasts im lokalen Subnet verwenden, da Router üblicherweise nicht konfiguriert sind Broadcasts weiterzuleiten und Dein Broadcast Dein Subnet deshalb nicht verlassen wird.
Den Empfangssocket binde ich übrigens so:
void UdpSocket::bind(unsigned short port) { // Binden memset(&localAddr, 0, sizeof(localAddr)); localAddr.sin_family = AF_INET; localAddr.sin_port = htons( (u_short) port ); localAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); if (::bind(socket, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) throw SocketException(__FUNCTION__, "bind", ::WSAGetLastError()); }
(wobei localAddr ein Member vom Typ SOCKADDR_IN ist)
Noch ein Tip: Bei UDP gibts gegenüber TCP keine Rückbestätigung und auch kein "Puffer voll", d.h. wenn der Empfänger keine Daten annimmt (weil sein Puffer voll ist) dann werden die UDP Pakete still und heimlich verworfen. Der Empfangspuffer ist unter Winsock 8kb groß, kann aber über ::setsockopt(s, SOL_SOCKET, SO_RCVBUF, ...) eingestellt werden.
mfg
Andreas
- Bearbeitet Andreas Hammerschmidt Montag, 15. September 2014 05:14 typo
- Als Antwort markiert Fire-Heart Montag, 15. September 2014 07:55
-
Hallo Andreas
Vielen Dank für die Tips. Ich hatte das mittlerweile schon irgendwie zum Laufen gebracht.
Interessant ist die Angabe IPPROTO_UDP, weil ich mich eigentlich schon gewundert hatte, warum bei UDP Telegrammen 0 steht (bei TCP gibt die "Internet Literatur" immer IPPROTO_TCP an, aber bei UDP nur Null).
Die Sache mit der BroadCast Freigabe hatte ich mir mühsam selbst erarbeitet, da ich irgendwann den Fehlercode des Sende-Befehls ausgewertet hatte und wusste, warum es nicht funktioniert.
Wir kommunizieren über UDP mit einem Messmodul in beide Richtungen, wobei wir quasi auf gesendete Telegramme eine Antwort am anderen Port bekommen und somit wissen, ob das was wir gesendet haben, auch wirklich angekommen ist (bei Konfigurationstelegrammen z.B.).
Grüße
FireHeart