Winsock bind () a falhar com o WSAEADDRNOTAVAIL para o endereço de difusão directa
estou a configurar um socket UDP e tentar ligar o que deve ser um válido endereço de broadcast para ele (192.168.202.255 : 23456), mas bind
falhar com o erro 10049, WSAEADDRNOTAVAIL
. Se eu usar um endereço de transmissão local, 127.0.0.255, será bem sucedido.
WSAEADDRNOTAVAIL
'a documentação do s diz que" o endereço solicitado não é válido no seu contexto. Isto normalmente resulta de uma tentativa de vincular a um endereço que não é válido para o computador local. Isto também pode resultar from connect, sendto, WSAConnect, WSAJoinLeaf, or WSASendTo when the remote address or port is not valid for a remote computer (for example, address or port 0)."Mas eu acho que este endereço, 192.168.202.255, deve ser um endereço de difusão válido por causa da seguinte entrada ao executar ipconfig
:
Código
Sou novo na programação Winsock e estou provavelmente a cometer um erro elementar, mas não consigo encontrá-lo. O o código que tenho até agora é: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;
}
}
}
}
comentários
ParseIPAddress
é um invólucro em torno de inet_addr
; inspeccionando o valor de {[8] } parece estar correcto. m_oSocket
é um SOCKET
. Adicionei a chamada a setsockopt
uma vez que não pode transmitir nada além do TCP por omissão (ver o segundo parágrafo nas observações sendto
; esta chamada não faz qualquer diferença. PrintLine
é uma embalagem para a saída da consola. Os moldes odd String / c_str()
estão a converter de e para wstrings C++ para strings Unicode VCL, uma vez que I am usando C++ Builder e suas bibliotecas VCL. O endereço IP é uma cadeia estreita (char).
sendto
documentation states that " If a socket is opened, a setsockopt call is made, and then a sendto call is made, Windows Sockets performs an implícito bind function call."Isto implica que bind
não é necessário de todo. Se eu omitir a chamada, então ligarei assim:
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()));
falha com o erro 10047, WSAEAFNOSUPPORT
, "família de endereços não suportada pelo protocolo familia."
a produção de netsh winsock show catalog
(mencionado no final das observações de socket
) é longo, mas inclui várias entradas que mencionam o UDP e o IPv4.
sockaddr
, mas esta parece ser a prática normal para a programação Winsock quando eu tenho lido exemplos. A leitura pode ser necessária, uma vez que a interpretação subjacente depende da família do protocolo. Parece uma potencial fonte de erro, mas não sei como evitá-lo.
Alguma ideia? Estou perplexo.
configuração
- O Windows 7 Pro está a funcionar na fusão VMware 4.1.3
- o programa é compilado como 32 bits com Embarcadero C++ Builder 2010.
- o programa é apenas um programa de consola
2 answers
// Winsock 2.2 is supported in XP
Na verdade, Winsock 2.2 remonta a NT 4 SP4, que Data de 1998. Por causa disso, eu não me preocuparia em Verificar {[[2]} no caso de erro. Basicamente, não há hipótese de isto voltar a acontecer.
Se a portabilidade ampla for o teu objectivo, eu apontaria para Winsock 1.1, que é ... Tudo o que você precisa {[[31]} para o código que você mostra, e vai deixar o código construir e executar em qualquer coisa que suporte Winsock, mesmo de volta para o Windows 3.x.Mau estilo. Deve utilizar
m_oSocket = socket(AF_INET, SOCK_DGRAM /*UDP*/, IPPROTO_UDP);
PF_INET
aqui em vez de AF_INET
. Eles têm o mesmo valor, mas você não está especificando uma família de endereços (AF
) aqui, você está especificando uma família de protocolos (PF
). Além disso, o terceiro parâmetro pode ser zero com segurança, porque está implícito pelos dois primeiros parâmetros. Mais uma vez, é apenas um estilo conserta, não uma conserta funcional.
Sim. Não duvides dos médicos e usa o
int iBroadcast = true; // docs say int sized, but boolean values
bool
aqui. Lembre-se, Winsock é baseado em bases BSD, e isso remonta aos dias antes do C++ existir.
Não devias estar a investigar os internos da estrutura desta maneira. A API sockets tem um atalho para isso, que é mais curto e esconde alguns dos detalhes de implementação interna. É:
m_oServer.sin_addr.S_un.S_addr = m_ulAddress;
Continuando...
m_oServer.sin_addr.s_addr = m_ulAddress;
Apesar do Remy ter razão em dizer que a chamada não está correcta, não precisas mesmo dela. Você pode depender da camada de roteamento do seu sistema para enviar o pacote para fora da interface correta. Não precisas de "ajudar"com uma chamada.
if (bind(m_oSocket, ...
Percebeste mal o que eu disse. A MSDN está a dizer-te. Quando você vê o termo "TCP / IP", muitas vezes (mas nem sempre!) inclui UDP. Estão a usá-lo nesse sentido genérico.Só pode transmitir por defeito através do TCP (ver o segundo parágrafo nas observações do sendto);
O bit MSDN que aponta para falar sobre TCP / IP porque o Winsock foi criado num mundo em que o TCP/IP ainda não tinha vencido as guerras do protocolo de rede. Eles estão tentando restringir a discussão para TCP / IP (UDP, na verdade) para que você não tenha a idéia de que o que eles estão dizendo se aplica a outros transportes de rede suportados por pilhas Winsock nos primeiros dias: NetBIOS, IPX, DECNet...
Na verdade, você pode apenas transmitir (ou multicast) usando 'sockets' UDP. TCP é ponto-a-ponto, apenas.
Uma coisa que me parece duvidosa é lançar o 'SOCKET m_oSocket' para sockaddr.Isso também faz parte do suporte de transporte de várias redes em tomadas. Além de
sockaddr_in
, há sockaddr_ipx
para IPX, sockaddr_dn
para DECnet... Winsock é uma API C, Não uma API C++, por isso não podemos subclassar sockaddr
e passar uma referência ao classe base, ou criar sobrecarga de função para cada uma das variações. Este truque de estruturas de fundição é uma maneira típica de obter um tipo de polimorfismo.
Aqui está um exemplo de trabalho, que se constrói com o MinGW., g++ foo.cpp -o foo.exe -lwsock32
:
#include <winsock.h>
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char* argv[])
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(1, 1), &wsa)) {
cerr << "Failed to init Winsock!" << endl;
return 1;
}
// Get datagram socket to send message on
SOCKET sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
cerr << "socket() failed: " << WSAGetLastError() << endl;
return 1;
}
// Enable broadcasts on the socket
int bAllow = 1;
if (setsockopt(sd, SOL_SOCKET, SO_BROADCAST, (char*)&bAllow,
sizeof(bAllow)) < 0) {
cerr << "setsockopt() failed: " << WSAGetLastError() << endl;
closesocket(sd);
return 1;
}
// Broadcast the request
string msg = "Hello, world!";
const int kMsgLen = msg.length();
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
const uint16_t kPort = 54321;
sin.sin_port = htons(kPort);
sin.sin_family = AF_INET;
if (argc == 1) {
sin.sin_addr.s_addr = INADDR_BROADCAST;
}
else if ((sin.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE) {
cerr << "Couldn't parse IP '" << argv[1] << "'!" << endl;
}
int nBytes = sendto(sd, msg.c_str(), kMsgLen, 0,
(sockaddr*)&sin, sizeof(struct sockaddr_in));
closesocket(sd);
// How well did that work out, then?
if (nBytes < 0) {
cerr << "sendto() IP " << inet_ntoa(sin.sin_addr) <<
" failed" << WSAGetLastError() << endl;
return 1;
}
else if (nBytes < kMsgLen) {
cerr << "WARNING: Short send, " << nBytes << " bytes! "
"(Expected " << kMsgLen << ')' << endl;
return 1;
}
else {
cerr << "Sent " << kMsgLen << "-byte msg to " <<
inet_ntoa(sin.sin_addr) << ':' << kPort << '.' << endl;
}
return 0;
}
Ele envia para 255.255.255.255 (INADDR_BROADCAST
) por padrão, mas se você passar um IP de transmissão dirigida (como o seu valor 192.168.202.255) como o primeiro parâmetro, ele vai usar isso em vez disso.
Você não deve {[[0]} para um endereço IP de transmissão. Você precisa de {[[0]} para um IP Adaptador de rede individual. Se você quiser enviar uma mensagem de Transmissão, você bind()
para o adaptador que vai enviar a transmissão, e então sendto()
o IP de transmissão. Se quiser receber uma mensagem de Transmissão, você bind()
para o adaptador específico para o qual o IP corresponde ao IP de transmissão para o qual está a ser enviado.