2017-01-17 3 views
5

Mon application Delphi Winsock doit écouter sur toutes les interfaces réseau le flux UDP/IP multidiffusion. Il a écouté normalement jusqu'à ce que je l'ai essayé sur un autre PC avec l'ordre de priorité des différents adaptateurs de réseau.Différence entre INADDR_ANY sous Linux et la programmation de socket Windows

Alors j'ai commencé à problème de recherche et a trouvé sur certains forums qui INADDR_ANY (ou 0.0.0.0) a un sens différent dans Windows et Linux:

Pourriez-vous confirmer ou infirmer cela?

Comment écouter vraiment sur toutes les interfaces?

Voici un petit morceau de mon code:

TMulticastListener = class(TThread) 
private 
    mreq: ip_mreq; 
    ............ 
end; 

constructor TMulticastListener.Create; 
var err: Integer; 
    wData: WsaData; 
    reuse: Integer; 
begin 
    inherited Create(true); 

    err := WSAStartup(MAKEWORD(2, 2), wData); 
    if err = SOCKET_ERROR then begin 
    // Tell the user that we could not find a usable Winsock DLL 
    perror('WSAStartup'); 
    Exit; 
    end; 

    // create what looks like an ordinary UDP socket 
    fd := socket(AF_INET, SOCK_DGRAM, 0); 
    if fd = INVALID_SOCKET then begin 
    perror('socket'); 
     Exit; 
    end; 

    reuse := 1; 

    // allow multiple sockets to use the same PORT number 
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, Pointer(@reuse), SizeOf(reuse)) < 0) then begin 
    perror('Reusing ADDR failed'); 
    Exit; 
    end; 

    // set up destination address 
    FillChar(addr, sizeof(addr), 0); 
    addr.sin_family := AF_INET; 
    addr.sin_addr.s_addr := htonl(INADDR_ANY); // N.B.: differs from sender 
    addr.sin_port := htons(HELLO_PORT); 

    // bind to receive address 
    if (bind(fd, addr, sizeof(addr)) < 0) then begin 
     perror('bind'); 
     Exit; 
    end; 

    // use setsockopt() to request that the kernel join a multicast group 
    mreq.imr_multiaddr.s_addr := inet_addr(HELLO_GROUP); 
    mreq.imr_interface.s_addr := htonl(INADDR_ANY); //inet_addr('0.0.0.0'); 
    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @mreq, sizeof(mreq)) < 0) then begin 
     perror('setsockopt'); 
     Exit; 
    end; 
end; 

Répondre

3

Windows et Linux se comportent en fait le même en ce qui concerne l'utilisation de INADDR_ANY . La confusion est ici parce que les deux liens que vous fournissez sont utilisés dans différents contextes.Lorsque vous utilisez la fonction bind pour lier une adresse/un port, la spécification INADDR_ANY signifie que le socket pourra recevoir des paquets sur le port donné à partir de n'importe quelle interface. Cependant, cela ne pas mis en place quoi que ce soit en ce qui concerne la multidiffusion.

Dans le cadre de l'appel à setsockoptIP_ADD_MEMBERSHIP, réglage de l'interface INADDR_ANY aura le système se joindre au groupe de multidiffusion donné sur l'interface réseau par défaut.

Le lien Linux que vous avez donné fait référence à bind, tandis que le lien Windows fait référence à setsockopt et IP_ADD_MEMBERSHIP.

Si vous souhaitez rejoindre le groupe de multidiffusion sur toutes les interfaces, vous devez récupérer la liste des interfaces sur le système et les joindre. Sous Windows, la fonction GetAdaptersAddresses() vous donnera la liste des interfaces. Sous Linux, utilisez la fonction getifaddrs().

Voici un exemple de la façon d'utiliser la fonction GetAdaptersAddresses() dans C:

struct iflist { 
    char name[50]; 
    struct sockaddr_in sin; 
    int isloopback; 
    int ismulti; 
    int ifidx; 
}; 

void getiflist(struct iflist *list, int *len) 
{ 
    IP_ADAPTER_ADDRESSES *head, *curr; 
    IP_ADAPTER_UNICAST_ADDRESS *uni; 
    char *buf; 
    int buflen, err, i; 

    buflen = 100000; 
    buf = calloc(buflen, 1); 
    head = (IP_ADAPTER_ADDRESSES *)buf; 
    if ((err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, head, 
            &buflen)) != ERROR_SUCCESS) { 
     char errbuf[300]; 
     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 
         0, errbuf, sizeof(errbuf), NULL); 
     printf("GetAdaptersAddresses failed: (%d) %s", err, errbuf); 
     free(buf); 
     return; 
    } 
    for (*len = 0, curr = head; curr; curr = curr->Next) { 
     if (curr->IfType == IF_TYPE_TUNNEL) continue; 
     for (uni = curr->FirstUnicastAddress; uni; uni = uni->Next) { 
      if (curr->OperStatus == IfOperStatusUp) { 
       memset(&list[*len], 0, sizeof(struct iflist)); 
       strncpy(list[*len].name, (char *)curr->AdapterName, 
         sizeof(list[i].name) - 1); 
       memcpy(&list[*len].sin, uni->Address.lpSockaddr, 
         uni->Address.iSockaddrLength); 
       list[*len].isloopback = 
         (curr->IfType == IF_TYPE_SOFTWARE_LOOPBACK); 
       list[*len].ismulti = 
         ((curr->Flags & IP_ADAPTER_NO_MULTICAST) == 0); 
       if (uni->Address.lpSockaddr->sa_family == AF_INET6) { 
        list[*len].ifidx = curr->Ipv6IfIndex; 
       } else { 
        list[*len].ifidx = curr->IfIndex; 
       } 
       (*len)++; 
      } 
     } 
    } 
    free(buf); 
} 
+0

Merci. J'ai fourni le code complet. Va lire le tien à la maison. – Paul

0

votre source est complètement inconscient du fait que le protocole Internet lui-même ne sait rien sur les « ports » et « interfaces », la déclaration précitée (« écouter toutes les interfaces ») ne marche même pas de sens, son complètement composé mais paquets à diffuser les adresses sont généralement acheminés sur plusieurs interfaces, voir plus bas:

0.0.0.0 est un spe cial, adresse IPv4 réservée appelée "identificateur de réseau" - en fait, les adresses IPv4 qui se terminent par 0 sont généralement réservées - it is typically not usable except for broadcast and network purposes. Systèmes d'exploitation se réservent généralement 0.0.0.0 pour les émissions dans l'unique transport protocol

maintenant: ces adresses de diffusion reçoivent toujours des émissions pour un seul protocole de transport par la route par défaut qui peut pointer vers plusieurs (ou toutes) interfaces réseau. Qu'est-ce que vous avez probablement lisiez à propos de quelque chose de complètement différent: Multicast - des thats encore une autre boîte de Pandore, il est possible d'envoyer paquets singuliers à plusieurs, désignés récepteurs - Microsoft Windows a un routage multidiffusion par défaut et Linux a généralement être configuré pour la multidiffusion afin de travailler (AFAIK) - mais vous ne voulez pas cela.

Conclusion: pour vos besoins, 0.0.0.0 est identique sous Windows et Linux - son adresse broadcast pour votre protocole de transport choisi, il n'y a pas de différence