Winsock-bind() nicht mit WSAEADDRNOTAVAIL für die gerichtete broadcast-Adresse
Ich bin einrichten einen UDP-socket und versucht zu binden, was sollte eine gültige Netzwerk-broadcast-Adresse, es (192.168.202.255 : 23456), aber bind
schlägt fehl mit Fehler 10049, WSAEADDRNOTAVAIL
. Wenn ich einen localhost broadcast-Adresse, 127.0.0.255, gelingt es.
WSAEADDRNOTAVAIL
's-Dokumentation sagt, dass "Die angeforderte Adresse ist in diesem Kontext ungültig. Diese Regel Ergebnisse aus einem Versuch zum binden an eine Adresse, die nicht gültig für den lokalen computer. Dies kann auch die Folge von zu verbinden, sendto, WSAConnect WSAJoinLeaf oder WSASendTo, wenn der remote-Adresse oder der port ist nicht gültig für einen Remotecomputer (Z. B. Adresse oder port 0)." Aber ich denke diese Adresse, 192.168.202.255, sollte eine gültige broadcast-Adresse, da der folgende Eintrag beim laufen ipconfig
:
Was das problem sein könnte?
Code
Ich bin neu auf Winsock-Programmierung und bin wahrscheinlich machen Sie einen elementaren Fehler, aber ich kann es nicht finden. Den code habe ich bisher:
m_ulAddress = ParseIPAddress(strAddress);
//Winsock 2.2 is supported in XP
const WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA oWSAData;
const int iError = WSAStartup(wVersionRequested, &oWSAData);
if (iError != 0) {
PrintLine(L"Error starting the network connection: WSAStartup error " + IntToStr(iError));
} else if (LOBYTE(oWSAData.wVersion) != 2 || HIBYTE(oWSAData.wVersion) != 2) {
PrintLine(L"Error finding version 2.2 of Winsock; got version " + IntToStr(LOBYTE(oWSAData.wVersion)) + L"." + IntToStr(HIBYTE(oWSAData.wVersion)));
} else {
m_oSocket = socket(AF_INET, SOCK_DGRAM /*UDP*/, IPPROTO_UDP);
if (m_oSocket == INVALID_SOCKET) {
PrintLine(L"Error creating the network socket");
} else {
//Socket needs to be able to send broadcast messages
int iBroadcast = true; //docs say int sized, but boolean values
if (setsockopt(m_oSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&iBroadcast, sizeof(iBroadcast)) != 0) {
PrintLine(L"Error setting socket to allow broadcast addresses; error " + IntToStr(WSAGetLastError()));
} else {
m_oServer.sin_family = AF_INET;
m_oServer.sin_port = m_iPort;
m_oServer.sin_addr.S_un.S_addr = m_ulAddress;
//!!! This is the failing call
if (bind(m_oSocket, (sockaddr*)&m_oServer, sizeof(m_oServer)) == -1) {
PrintLine(L"Error binding address " + String(strAddress.c_str()) + L":" + IntToStr(m_iPort) + L" to socket; error " + IntToStr(WSAGetLastError()));
} else {
m_bInitialisedOk = true;
}
}
}
}
Kommentare
ParseIPAddress
ist ein wrapper um inet_addr
; Prüfung der Wert der m_oServer.sin_addr.S_un.S_addr
es richtig zu sein scheint. m_oSocket
ist ein SOCKET
. Ich habe den Aufruf setsockopt
da kann man keine Sendung über nichts, aber TCP standardmäßig (siehe zweiten Absatz in sendto
's Bemerkungen); dieser Aufruf macht keinen Unterschied. PrintLine
ist ein wrapper für die console-Ausgabe. Die ungeraden String /c_str()
wirft konvertieren zu und von C++ wstrings um VCL Unicode-strings, da ich mich mit C++ Builder und dessen VCL-Bibliotheken. Die IP-Adresse ist eine schmale (char) string.
Den sendto
- Dokumentation besagt, dass "Wenn ein socket geöffnet wird, einen setsockopt-Aufruf gemacht wird, und dann einem sendto-Aufruf gemacht wird, ist Windows-Sockets führt eine implizite bind-Funktion aufrufen." Dies impliziert, dass bind
ist überhaupt nicht notwendig. Wenn ich das weglasse, die rufen, dann ruft sendto
etwa so:
const int iLengthBytes = strMessage.length() * sizeof(char); //Narrow string
const int iSentBytes = sendto(m_oSocket, strMessage.c_str(), iLengthBytes, 0, (sockaddr*)&m_oServer, sizeof(m_oServer));
if (iSentBytes != iLengthBytes) {
PrintLine(L"Error sending network message; error: " + IntToStr(WSAGetLastError()));
schlägt fehl mit Fehler 10047, ALS
, "Address family not supported by protocol family."
Die Ausgabe von netsh winsock show catalog
(bereits an der Unterseite von socket
's Bemerkung) ist langwierig, aber enthält auch mehrere Einträge zu erwähnen, UDP und IPv4.
Eine mögliche Komplikation ist, dass diese läuft in einer VMWare Fusion host; Fusion macht eine ungerade setup für Netzwerke. Ich habe auch einen Cisco-VPN konfiguriert läuft zurück in mein Büro. Anschließen und trennen das macht keinen Unterschied.
Einer Sache scheint vertrackt zu mir ist schwer-casting des SOCKET
m_oSocket zu sockaddr
, aber das scheint normal zu sein-Praxis für Winsock-Programmierung, als ich gelesen habe Beispiele. Lesen es kann erforderlich sein, da die zu Grunde liegenden interpretation hängt von der Protokoll-Familie. Es scheint eine mögliche Fehlerquelle, aber ich bin mir nicht sicher, wie es zu vermeiden.
Irgendwelche Ideen? Ich bin ratlos 🙂
Setup
- Windows 7 Pro unter VMWare Fusion 4.1.3
- Das Programm kompiliert wird als 32-bit mit Embarcadero C++ Builder 2010.
- Das Programm ist ein Konsolenprogramm nur
Du musst angemeldet sein, um einen Kommentar abzugeben.
Viel Verwirrung hier. Ich kläre es Punkt für Punkt für Ihre Erbauung, aber wenn Sie wollen einfach nur funktionierenden code, zum Ende springen.
Eigentlich Winsock 2.2 geht zurück auf NT 4 SP4, die Termine es bis 1998. Weil das so ist, würde ich nicht die Mühe zu prüfen
oWSAData.wVersion
im Fehler-Fall. Es gibt im Grunde keine chance, dies passiert nicht mehr.Wenn Breite Portabilität Ihr Ziel ist, würde ich den Gegner Winsock 1.1, welche alles, was Sie brauchen für den code, den Sie zeigen, und Sie können den code erstellen und ausführen auf alles, was unterstützt, Winsock, sogar zurück zu Windows-3.x.
Schlechter Stil. Sollten Sie verwenden
PF_INET
hier stattAF_INET
. Sie haben den gleichen Wert, aber Sie sind nicht die Angabe einer Adresse Familie (AF
) hier, sind Sie unter Angabe einer Protokoll-Familie (PF
). Auch der Dritte parameter kann sicher null sein, weil es impliziert durch die ersten beiden Parameter. Wieder, es ist nur ein style-Update, nicht um eine funktionelle Korrektur.Yup. Keine zweiten raten die docs verwenden
bool
hier. Denken Sie daran, Winsock basiert auf BSD-sockets, und das geht zurück zu den Tagen, bevor C++ existierte.Sollten Sie wirklich nicht Graben in die Interna der
sockaddr_in
Struktur auf diese Weise. Die Socket-API ist eine Abkürzung, die kürzer ist und verbirgt einige von den internen Implementierungsdetails. Es ist:Umzug auf...
Obwohl Remy ist richtig, dass die
bind()
Aufruf nicht korrekt ist, braucht man eigentlich nicht, es überhaupt nicht. Sie können abhängen, die auf Ihrem system die routing-Schicht, um das Paket zu senden, das richtige interface. Sie brauchen nicht zu "helfen", es mit einerbind()
nennen.Hast du was falsch verstanden MSDN sagt Sie. Wenn Sie sehen, der Begriff "TCP/IP", ist es oft (aber nicht immer!) enthält UDP. Sie verwenden es in diesem Allgemeinen Sinn hier.
Den MSDN-bit-Punkt-zu Gesprächen über TCP/IP Winsock, weil die erstellt wurde, in einer Welt, die, wenn TCP/IP noch nicht gewonnen, das Netzwerkprotokoll, das wars. Versuchen Sie, beschränken Sie die Diskussion auf TCP/IP (UDP, wirklich), so dass Sie nicht auf die Idee kommen, dass das, was Sie sagen, gilt auch für andere Netzwerk-Transporte unterstützt von Winsock-stacks in den frühen Tagen: NetBIOS, IPX, DECNet...
In der Tat, können Sie nur broadcast (multicast -) UDP-sockets. TCP ist eine Punkt-zu-Punkt, nur.
Das ist auch Teil der mehreren Netzwerk-transport-Unterstützung für sockets. Neben
sockaddr_in
gibt essockaddr_ipx
für IPXsockaddr_dn
für DECnet... Winsock ist eine API in C, nicht C++ - API, so können wir nicht Unterklassesockaddr
und übergeben Sie einen Verweis auf die Basisklasse, oder erstellen Sie Funktions-überladungen für jede der Varianten. Dieser trick der casting-Strukturen ist ein typisches C Weg, um eine Art von Polymorphismus.Hier ist ein funktionierendes Beispiel, baut mit MinGW,
g++ foo.cpp -o foo.exe -lwsock32
:Sendet es an 255.255.255.255 (
INADDR_BROADCAST
) standardmäßig, aber wenn Sie übergeben eine gerichtete broadcast-IP (wie 192.168.202.255 Wert) als der erste parameter, wird stattdessen verwenden.bind
, und fügte hinzuhtons(port)
(ich vergaß die Netzwerk-byte-Reihenfolge für die port-Nummer vor!) und jetzt ist es alles funktioniert wunderbar. Vielen Dank für Ihre Hilfe!Sollten Sie nicht
bind()
auf eine broadcast-IP-Adresse. Sie müssenbind()
auf eine einzelne Netzwerk-adapter-IP statt. Wenn Sie möchten, senden Sie eine broadcast-Nachricht, die Siebind()
an den adapter zu senden die Sendung, und dannsendto()
die broadcast-IP. Wenn Sie möchten, erhalten eine broadcast-Nachricht, die Siebind()
auf die spezifischen adapter, dessen IP-entspricht der broadcast-IP gesendet wird.