11
  1. 您有多个网络适配器。
  2. 将 UDP 套接字绑定到本地端口,而不指定地址。
  3. 在其中一个适配器上接收数据包。

你如何获得接收数据包的适配器的本地 IP 地址?

问题是,“接收适配器的 IP 地址是什么?” 不是我们从发件人那里得到的地址

receive_from( ..., &senderAddr, ... );

称呼。

4

7 回答 7

4

您可以枚举所有网络适配器,获取它们的 IP 地址,并将子网掩码覆盖的部分与发送者的地址进行比较。

像:

IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
    foreach( adapter in EnumAllNetworkAdapters() )
    {
        adapterSubnet = adapter.subnetmask & adapter.ipaddress;
        senderSubnet = adapter.subnetmask & senderAddr;
        if( adapterSubnet == senderSubnet )
        {
            return adapter.ipaddress;
        }
    }
}
于 2008-08-27T11:24:50.680 回答
4

timbo提供的解决方案假设地址范围是唯一的并且不重叠。虽然这通常是这种情况,但它不是一个通用的解决方案。

在 Steven 的书“Unix 网络编程”(第 20.2 节)中提供了一个很好的函数实现,它是基于 recvmsg() 而不是 recvfrom() 的函数。如果您的套接字启用了 IP_RECVIF 选项,则 recvmsg() 将返回接收数据包的接口的索引。然后可以使用它来查找目标地址。

源代码可在此处获得。有问题的函数是'recvfrom_flags()'

于 2008-09-20T12:57:36.210 回答
3

天,

我假设您已经使用 INADDR_ANY 完成了绑定以指定地址。

如果是这种情况,那么 INADDR_ANY 的语义就是在所有接口上指定的端口上创建一个 UDP 套接字。套接字将获取发送到指定端口上所有接口的所有数据包。

使用此套接字发送时,使用编号最小的接口。传出发送方的地址字段设置为使用的第一个传出接口的 IP 地址。

第一个传出接口定义为执行 ifconfig -a 时的序列。它可能是 eth0。

HTH。

干杯,罗伯

于 2008-08-27T11:28:46.740 回答
1

不幸的是,当与绑定到“任何 IP”的套接字一起使用时,sendto 和 recvfrom API 调用从根本上被破坏了,因为它们没有本地 IP 信息的字段。

所以你对此能做些什么?

  1. 您可以猜测(例如基于路由表)。
  2. 您可以获取本地地址列表并将单独的套接字绑定到每个本地地址。
  3. 您可以使用支持此信息的较新 API。这有两个部分,首先您必须使用相关套接字选项(ip_recvif 用于 IPv4,ipv6_recvif 用于 IPv6)告诉堆栈您需要此信息。然后你必须使用不同的函数(linux 上的 recvmsg 和其他几个类似 unix 的系统,windows 上的 WSArecvmsg)来接收数据包。

这些选项都不是很好。猜测显然有时会产生错误的答案。绑定单独的套接字会增加软件的复杂性,并且如果本地地址列表发生更改,您的程序正在运行,则会导致问题。较新的 API 是正确的技术解决方案,但可能会降低可移植性(特别是看起来 WSArecvmsg 在 Windows XP 上不可用),并且可能需要修改您正在使用的套接字包装库。

编辑看起来我错了,似乎 MS 文档具有误导性,并且 WSArecvmsg 在 Windows XP 上可用。见https://stackoverflow.com/a/37334943/5083516

于 2016-08-13T22:44:36.943 回答
0

您可以先将 SO_REUSEADDR 设置为 true

BOOL bOptVal = 1;
setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));

创建另一个套接字后receive_from( ..., &remoteAddr, ... );,并连接回remoteAddr。然后调用getsockname可以得到ip地址。

SOCKET skNew = socket( )
// Same local address and port as that of your first socket 
// INADDR_ANY
bind(skNew, ,  )
// set SO_REUSEADDR to true again
setsockopt(skNew, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));

// connect back  
connect(skNew, remoteAddr)

// get local address of the socket
getsocketname(skNew, )
于 2020-09-02T04:43:04.527 回答
-3
大小_t
     recvfrom(int socket, void *restrict buffer, size_t length, int flags,
         struct sockaddr *restrict address, socklen_t *restrict address_len);

     大小_t
     recvmsg(int socket, struct msghdr *message, int flags);

[..]
     如果地址不是空指针并且套接字不是面向连接的,则
     填写消息的源地址。

实际代码:

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));

希望这可以帮助。

于 2008-08-27T11:18:34.473 回答
-4

试试这个:

gethostbyname("localhost");
于 2010-10-15T20:49:43.023 回答