常量 INADDR_ANY 就是所谓的 IPv4 通配符地址。这 通配符 IP 地址对于绑定 Internet 的应用程序很有用 多宿主主机上的域套接字。如果多宿主上的应用程序 主机将套接字绑定到其主机的 IP 地址之一,然后 套接字只能接收UDP数据报或发送的TCP连接请求 到该 IP 地址。然而,我们通常想要一个应用程序 多宿主主机能够接收数据报或连接请求 指定任何主机的 IP 地址,并将套接字绑定到 通配符 IP 地址使这成为可能。
struct sockaddr_in server_address;
int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_address, 0, sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY); // here is my quesion
server_address.sin_port = htons(9734);
bind(server_sockfd, (struct sockaddr*)&server_address, sizeof(server_address));
问题>
如果我们将套接字绑定到特定的IP地址,那么套接字只能接收发送到该IP地址的UDP/TCP请求。
正如我在上面的代码中所示,现在套接字
server_sockfd
与 INADDR_ANY
绑定。
我只是在这里感到困惑,因为如果套接字可以接收互联网上的任何请求,它如何仍然可以正常工作?互联网上有大量的UDP/TCP请求;如果套接字对每个人都有响应,它怎么还能工作?
客户端代码:
int
main(int argc, char *argv[])
{
struct sockaddr_in6 svaddr;
int sfd, j;
size_t msgLen;
ssize_t numBytes;
char resp[BUF_SIZE];
if (argc < 3 || strcmp(argv[1], "--help") == 0)
usageErr("%s host-address msg...\n", argv[0]);
/* Create a datagram socket; send to an address in the IPv6 somain */
sfd = socket(AF_INET6, SOCK_DGRAM, 0); /* Create client socket */
if (sfd == -1)
errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in6));
svaddr.sin6_family = AF_INET6;
svaddr.sin6_port = htons(PORT_NUM);
if (inet_pton(AF_INET6, argv[1], &svaddr.sin6_addr) <= 0)
fatal("inet_pton failed for address '%s'", argv[1]);
/* Send messages to server; echo responses on stdout */
for (j = 2; j < argc; j++) {
msgLen = strlen(argv[j]);
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in6)) != msgLen)
fatal("sendto");
numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
if (numBytes == -1)
errExit("recvfrom");
printf("Response %d: %.*s\n", j - 1, (int) numBytes, resp);
}
exit(EXIT_SUCCESS);
}
服务器代码:
int
main(int argc, char *argv[])
{
struct sockaddr_in6 svaddr, claddr;
int sfd, j;
ssize_t numBytes;
socklen_t len;
char buf[BUF_SIZE];
char claddrStr[INET6_ADDRSTRLEN];
/* Create a datagram socket bound to an address in the IPv6 somain */
sfd = socket(AF_INET6, SOCK_DGRAM, 0);
if (sfd == -1)
errExit("socket");
memset(&svaddr, 0, sizeof(struct sockaddr_in6));
svaddr.sin6_family = AF_INET6;
svaddr.sin6_addr = in6addr_any; /* Wildcard address */
svaddr.sin6_port = htons(PORT_NUM);
if (bind(sfd, (struct sockaddr *) &svaddr,
sizeof(struct sockaddr_in6)) == -1)
errExit("bind");
/* Receive messages, convert to uppercase, and return to client */
for (;;) {
len = sizeof(struct sockaddr_in6);
numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
(struct sockaddr *) &claddr, &len);
if (numBytes == -1)
errExit("recvfrom");
/* Display address of client that sent the message */
if (inet_ntop(AF_INET6, &claddr.sin6_addr, claddrStr,
INET6_ADDRSTRLEN) == NULL)
printf("Couldn't convert client address to string\n");
else
printf("Server received %ld bytes from (%s, %u)\n",
(long) numBytes, claddrStr, ntohs(claddr.sin6_port));
for (j = 0; j < numBytes; j++)
buf[j] = toupper((unsigned char) buf[j]);
if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
numBytes)
fatal("sendto");
}
}
执行结果:
$ ./server_program & [1]31047 $ ./client_program ::1 ciao // 发送到本地主机上的服务器 服务器从 (::1, 32770) 接收到 4 个字节 回应 1:CIAO
它不会收到针对互联网上每个 IP 地址的请求(a),它会收到针对其服务的每个 IP 地址的请求。例如,它可能有多个 NIC,每个 NIC 都有一个单独的 IP 地址,或者可能有一个能够管理多个 IP 地址的 NIC(甚至可能有多个 NIC,每个 NIC 都能够处理多个 IP 地址。
要查看的关键片段是:
...我们通常希望多宿主主机上的应用程序能够接收指定任何 主机 IP 地址(我的斜体)的数据报或连接请求。
换句话说,您可能有一个多宿主设置,其中您的机器提供服务
10.0.0.15
和 10.0.0.16
。使用 INADDR_ANY
将允许您获取这两个地址的流量,无需 获取对 10.0.0.17
的请求,这可能是工作台另一端(或地球另一端)的机器。
下表的顶行是请求目的地,左列是您正在侦听的地址,显示您是否会收到请求 (
Y
) 或不会 (N
):
Request to> 10.0.0.15 10.0.0.16 10.0.0.17
Bind to: *-------------------------------
10.0.0.15 | Y N N
10.0.0.16 | N Y N
INADDR_ANY | Y Y N
(a) 它甚至没有看到网络上的绝大多数请求。绝大多数甚至无法到达您最近的路由器(甚至可能到达您的 ISP)。即使那些 do 到达您最近的路由器,您的特定计算机也可能看不到它们是否发往本地网段上的另一台计算机(尽管存在混杂模式)。