如何从 udp-socket (C/C++) 获取您自己的(本地)IP 地址
Posted
技术标签:
【中文标题】如何从 udp-socket (C/C++) 获取您自己的(本地)IP 地址【英文标题】:How to get your own (local) IP-Address from an udp-socket (C/C++) 【发布时间】:2010-09-06 23:47:27 【问题描述】:-
您有多个网络适配器。
将 UDP 套接字绑定到本地端口,无需指定地址。
在其中一个适配器上接收数据包。
如何获取收到数据包的适配器的本地IP地址?
问题是,“接收适配器的 IP 地址是什么?”不是我们在
中获得的发件人地址receive_from( ..., &senderAddr, ... );
打电话。
【问题讨论】:
【参考方案1】:您可以枚举所有网络适配器,获取它们的 IP 地址,并将子网掩码覆盖的部分与发送者的地址进行比较。
喜欢:
IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
foreach( adapter in EnumAllNetworkAdapters() )
adapterSubnet = adapter.subnetmask & adapter.ipaddress;
senderSubnet = adapter.subnetmask & senderAddr;
if( adapterSubnet == senderSubnet )
return adapter.ipaddress;
【讨论】:
仅当数据包源自适配器的子网时。如果它被路由到那里就不会...... 是的,Len 是对的。如果数据包通过本地路由器上的 NAT 到达,您只会看到本地 IP(类似于 10.0.0.2) 是不是广播没有路由(只在本地发送)?因此,IIRC 该方法应该可以工作。 它适用于广播,但广播远非服务器可能接收的唯一数据包。它不适用于具有多个 IP 地址的服务器的一般情况,其中任何一个都可能从 Internet 上的任何地方接收数据包。【参考方案2】:timbo 提供的解决方案假定地址范围是唯一的且不重叠。虽然通常是这种情况,但它不是通用解决方案。
在 Steven 的书“Unix 网络编程”(第 20.2 节)中提供了一个出色的函数实现,它完全符合您的要求 这是一个基于 recvmsg() 而不是 recvfrom() 的函数。如果您的套接字启用了 IP_RECVIF 选项,则 recvmsg() 将返回接收数据包的接口的索引。然后可以使用它来查找目标地址。
源代码可用here。有问题的函数是'recvfrom_flags()'
【讨论】:
我将对此进行调查。也许这是一个更好的解决方案。【参考方案3】:生日,
我假设您已使用 INADDR_ANY 完成绑定以指定地址。
如果是这种情况,那么 INADDR_ANY 的语义就是在所有接口上指定的端口上创建一个 UDP 套接字。套接字将获取发送到指定端口上所有接口的所有数据包。
使用此套接字发送时,使用编号最小的接口。传出发件人的地址字段设置为使用的第一个传出接口的 IP 地址。
第一个传出接口定义为执行 ifconfig -a 时的序列。它可能是 eth0。
HTH。
干杯, 抢
【讨论】:
【参考方案4】:不幸的是,当与绑定到“任何 IP”的套接字一起使用时,sendto 和 recvfrom API 调用从根本上被破坏了,因为它们没有本地 IP 信息的字段。
那么你能做些什么呢?
-
您可以猜测(例如根据路由表)。
您可以获取本地地址列表并将单独的套接字绑定到每个本地地址。
您可以使用支持此信息的更新 API。这有两个部分,首先您必须使用相关套接字选项(ip_recvif 用于 IPv4,ipv6_recvif 用于 IPv6)告诉堆栈您需要此信息。然后你必须使用不同的函数(linux 上的 recvmsg 和其他几个类似 unix 的系统,windows 上的 WSArecvmsg)来接收数据包。
这些选项都不是很好。猜测显然有时会产生错误的答案。绑定单独的套接字会增加软件的复杂性,并且如果本地地址列表发生更改,您的程序正在运行,则会导致问题。较新的 API 是正确的技术解决方案,但可能会降低可移植性 (特别是看起来 WSArecvmsg 在 Windows XP 上不可用),并且可能需要修改您正在使用的套接字包装库。
编辑看起来我错了,似乎 MS 文档具有误导性,并且 WSarecvmsg 在 Windows XP 上可用。见https://***.com/a/37334943/5083516
【讨论】:
【参考方案5】:您可以先将 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, )
【讨论】:
【参考方案6】: 大小_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, "在本地地址 %s\n 上读取 %d 个字节", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));
希望这会有所帮助。
【讨论】:
源地址为远程地址【参考方案7】:试试这个:
gethostbyname("localhost");
【讨论】:
这只会得到 localhost 被配置为解析的内容(应该是 127.0.0.1).. 而不是 UDP 数据包的接收端。以上是关于如何从 udp-socket (C/C++) 获取您自己的(本地)IP 地址的主要内容,如果未能解决你的问题,请参考以下文章
如何从两个版本之间的 Perforce diff 中获取 C/C++ 函数名
如何从 c/c++ 源代码获取适用于 Windows 的 python .pyd? (更新:如果你想要的话,现在在 Python 中很活跃)