Como obter seu próprio endereço IP (local) de um soquete udp (C/C++)
Pergunta
- Você tem vários adaptadores de rede.
- Vincule um soquete UDP a uma porta local, sem especificar um endereço.
- Receba pacotes em um dos adaptadores.
Como você obtém o endereço IP local do adaptador que recebeu o pacote?
A questão é: "Qual é o endereço IP do adaptador receptor?" não o endereço do remetente que obtemos no
receive_from( ..., &senderAddr, ... );
chamar.
Solução
Você poderia enumerar todos os adaptadores de rede, obter seus endereços IP e comparar a parte coberta pela máscara de sub-rede com o endereço do remetente.
Como:
IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
foreach( adapter in EnumAllNetworkAdapters() )
{
adapterSubnet = adapter.subnetmask & adapter.ipaddress;
senderSubnet = adapter.subnetmask & senderAddr;
if( adapterSubnet == senderSubnet )
{
return adapter.ipaddress;
}
}
}
Outras dicas
Bom dia,
Presumo que você tenha feito sua ligação usando INADDR_ANY para especificar o endereço.
Se for esse o caso, então a semântica de INADDR_ANY é tal que um soquete UDP é criado na porta especificada em todas as suas interfaces.O soquete receberá todos os pacotes enviados para todas as interfaces na porta especificada.
Ao enviar usando este soquete, a interface de número mais baixo é usada.O campo de endereço do remetente de saída é definido como o endereço IP da primeira interface de saída usada.
A primeira interface de saída é definida como a sequência quando você executa um ifconfig -a.Provavelmente será eth0.
HTH.
obrigada Roubar
A solução fornecida por timbó assume que os intervalos de endereços são exclusivos e não se sobrepõem.Embora geralmente seja esse o caso, não é uma solução genérica.
Há uma excelente implementação de uma função que faz exatamente o que você está atrás fornecido no livro de Steven "Unix network programming" (seção 20.2) Esta é uma função baseada em recvmsg(), em vez de recvfrom().Se o seu soquete tiver a opção IP_RECVIF habilitada então recvmsg() retornará o índice da interface na qual o pacote foi recebido.Isso pode então ser usado para procurar o endereço de destino.
O código fonte está disponível aqui.A função em questão é 'recvfrom_flags()'
Infelizmente, as chamadas de API sendto e recvfrom são fundamentalmente quebradas quando usadas com soquetes vinculados a "Qualquer IP" porque não possuem campo para informações de IP local.
Então, o que você pode fazer sobre isso?
- Você pode adivinhar (por exemplo, com base na tabela de roteamento).
- Você pode obter uma lista de endereços locais e vincular um soquete separado a cada endereço local.
- Você pode usar APIs mais recentes que oferecem suporte a essas informações.Há duas partes para isso: primeiro você deve usar a opção de soquete relevante (ip_recvif para IPv4, ipv6_recvif para IPv6) para informar à pilha que deseja essas informações.Então você tem que usar uma função diferente (recvmsg no Linux e vários outros sistemas do tipo Unix, WSArecvmsg no Windows) para receber o pacote.
Nenhuma dessas opções é ótima.Adivinhar obviamente produzirá respostas erradas às vezes.A vinculação de soquetes separados aumenta a complexidade do seu software e causa problemas se a lista de endereços locais for alterada se o seu programa estiver em execução.As APIs mais recentes são a solução técnica correta, mas podem reduzir a portabilidade (em particular, parece que WSArecvmsg não está disponível no Windows XP) e pode exigir modificações na biblioteca wrapper de soquete que você está usando.
Editar parece que eu estava errado, parece que a documentação do MS é enganosa e que WSArecvmsg está disponível no Windows XP.Ver https://stackoverflow.com/a/37334943/5083516
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len); ssize_t recvmsg(int socket, struct msghdr *message, int flags); [..] If address is not a null pointer and the socket is not connection-oriented, the source address of the message is filled in.
Código real:
int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);
fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));
espero que isto ajude.
Experimente isto:
gethostbyname("localhost");