Linux和Windows套接字编程中的INADDR_ANY之间的区别

我的Winsock Delphi应用程序应该监听所有networking接口上的多播UDP / IPstream。 它正常听,直到我在另一台PC上使用不同的networking适配器的优先顺序尝试它。

然后我开始研究问题,发现在一些论坛上, INADDR_ANY (或0.0.0.0 )在Windows和Linux中有不同的含义:

  • 在Linux中, 它意味着“侦听所有接口”并发送 – 通过默认接口发送
  • 在Windows中, 它意味着“在默认界面上侦听” (第二个为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); // NB: 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; 

Windows和Linux实际上在使用INADDR_ANY方面表现相同。 这里的困惑是因为你提供的两个链接正在不同的环境中使用

当使用bind函数绑定到地址/端口时,指定INADDR_ANY意味着套接字将能够从任何接口接收给定端口上的数据包。 但是,这样做不会设置有关多播的任何内容。

在调用setsockoptIP_ADD_MEMBERSHIP环境中,将接口设置为INADDR_ANY将使系统加入默认网络接口上给定的多播组。

您提供的Linux链接是指bind ,而Windows链接是指setsockoptIP_ADD_MEMBERSHIP

如果您想要加入所有接口上的组播组,您需要检索系统上的接口列表并加入每个接口。 在Windows上, GetAdaptersAddresses()函数将为您提供接口列表。 在Linux上,使用getifaddrs()函数。

下面是一个如何在C中使用GetAdaptersAddresses()函数的例子:

 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.0.0.0是一个特殊的,保留的IPv4地址,称为“网络标识符” – 事实上,以0结尾的IPv4地址通常是保留的 – 除了广播和网络目的外,它通常是不可用的 。 在一个传输协议中,操作系统通常保留0.0.0.0的广播

现在:这些广播地址总是通过可能指向多个(或全部)网络接口的默认路由接收单个传输协议的广播。 你可能读到的东西是完全不同的: 多播 – 这又是一个蠕虫的可能,有可能发送单个数据包到多个指定的接收器 – 微软Windows有一个默认的多播路由和Linux通常必须配置为多播为了工作(AFAIK) – 但你不想这样做。

结论 :为了您的目的,Windows和Linux上的0.0.0.0是相同的 – 它是您选择的传输协议的广播地址,没有区别