2017-01-17 2 views
5

Приложение My Winsock Delphi должно прослушивать все сетевые интерфейсы для многоадресного потока UDP/IP. Он слушал нормально, пока я не попробовал его на другом ПК с приоритетным порядком разных сетевых адаптеров.Разница между INADDR_ANY в Linux и программировании сокетов Windows

Тогда я начал задачи исследования и на некоторых форумах, что INADDR_ANY (или 0.0.0.0) имеет разное значение в ОС Windows и Linux:

  • В Linux it means "listen on all interfaces" и для отправки - отправить через интерфейс по умолчанию
  • В Windows it means "listen on default interface" (0.0.0.1 для второго). Образец цитирования: «Если этот член указывает IPv4-адрес 0.0.0.0, используется интерфейс многоадресной передачи по IPv4 по умолчанию» - без упоминания о том, предназначено ли оно для прослушивания или для отправки.

Не могли бы вы подтвердить или опровергнуть это?

Как слушать действительно на всех интерфейсах?

Вот маленький кусочек моего кода:

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; 

ответ

3

для Windows и Linux на самом деле ведут себя так же в отношении использования INADDR_ANY , Путаница здесь объясняется тем, что две приведенные вами ссылки используются в различных контекстах.

При использовании функции bind для привязки к адресу/порту, задание INADDR_ANY означает, что сокет сможет принимать пакеты на данном порту с любого интерфейса. Однако, делает так нет настроен что-нибудь относительно многоадресной рассылки.

В контексте IP_ADD_MEMBERSHIP звонок setsockopt, установка интерфейса INADDR_ANY приведет к присоединению системы к данной группе многоадресной передачи в сетевом интерфейсе по умолчанию.

Ссылка на Linux, которую вы указали, относится к bind, а ссылка Windows относится к setsockopt и IP_ADD_MEMBERSHIP.

Если вы хотите присоединиться к группе многоадресной передачи на всех интерфейсах, вам необходимо получить список интерфейсов в системе и присоединиться к каждой из них. В Windows функция GetAdaptersAddresses() предоставит вам список интерфейсов. В Linux используйте функцию getifaddrs().

Вот пример того, как использовать функцию GetAdaptersAddresses() в 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

Спасибо. Я предоставил полный код. Будут читать ваши дома. – Paul

0

ваш источник совершенно не обращая внимания на то, что сам по себе интернет-протокол ничего не знает о «портов» и «интерфейсы» не знаю, вышеупомянутое заявление («слушать на все интерфейсы») оленьей кожи даже никакого смысла, его полностью составлены но пакетов широковещательных адресов обычно маршрутизируется на несколько интерфейсов, больше на этом ниже:

0.0.0.0 является спом cial, зарезервированный адрес IPv4, называемый «Сетевой идентификатор» - фактически адреса IPv4, которые заканчиваются 0, как правило, зарезервированы - it is typically not usable except for broadcast and network purposes. Операционные системы обычно резервировать 0.0.0.0 для передач в пределах одного единственного transport protocol

Сейчас: эти широковещательные адреса всегда получать широковещательные для одного транспортного протокола через маршрут по умолчанию, который может указывать на нескольких (или всех) сетевых интерфейсов. То, что вы, вероятно, читали о том, что-то совершенно иначе: Multicast - вот и еще один может червей, можно отправить особые пакеты с несколькими, назначенных приемников - Microsoft Windows имеет маршрут многоадресного по умолчанию и Linux обычно имеет для настройки для многоадресной рассылки (AFAIK) - но вы этого не хотите.

Заключения: для ваших целей, 0.0.0.0 идентичен на Windows, и Linux - св broadcast адрес для выбранного транспортного протокола, нет никакой разницы

Смежные вопросы