如何从udp-socket(C / C ++)获取自己的(本地)IP地址

时间:2008-08-27 10:54:29

标签: c++ sockets udp

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

    问题是,“接收器适配器的IP地址是多少?”不是我们在

    中获得的发件人地址
    receive_from( ..., &senderAddr, ... );
    

    呼叫。

7 个答案:

答案 0 :(得分:3)

您可以枚举所有网络适配器,获取其IP地址,并将子网掩码所涵盖的部分与发件人的地址进行比较。

像:

IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
    foreach( adapter in EnumAllNetworkAdapters() )
    {
        adapterSubnet = adapter.subnetmask & adapter.ipaddress;
        senderSubnet = adapter.subnetmask & senderAddr;
        if( adapterSubnet == senderSubnet )
        {
            return adapter.ipaddress;
        }
    }
}

答案 1 :(得分:3)

天儿真好,

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

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

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

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

HTH。

欢呼声, 罗布

答案 2 :(得分:3)

timbo提供的解决方案假定地址范围是唯一的且不重叠。虽然情况通常如此,但它不是一般解决方案。

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

源代码可用here。有问题的函数是'recvfrom_flags()'

答案 3 :(得分:0)

不幸的是,当使用绑定到"任何IP"的套接字时,sendto和recvfrom API调用基本上被破坏了。因为他们没有本地IP信息的字段。

那么你能做些什么呢?

  1. 您可以猜测(例如,基于路由表)。
  2. 您可以获取本地地址列表,并将单独的套接字绑定到每个本地地址。
  3. 您可以使用支持此信息的较新API。这有两个部分,首先你必须使用相关套接字选项(IPv4的ip_recvif,IPv6的ipv6_recvif)告诉堆栈你想要这个信息。然后你必须使用一个不同的功能(在Linux上的recvmsg和其他几个类似unix的系统,Windows上的WSArecvmsg)来接收数据包。
  4. 这些选项都不是很好。猜测显然会产生错误的答案。绑定单独的套接字会增加软件的复杂性,如果本地地址列表发生更改,程序将运行,则会导致问题。较新的API是正确的技术解决方案但可能会降低可移植性(特别是它看起来像Windows XP上没有WSArecvmsg)并且可能需要修改您正在使用的套接字包装器库。

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

答案 4 :(得分: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, )

答案 5 :(得分:-3)

ssize_t
     recvfrom(int socket, void *restrict buffer, size_t length, int flags,
         struct sockaddr *restrict address, socklen_t *restrict address_len);

     ssize_t
     recvmsg(int socket, struct msghdr *message, int flags);

[..]
     If address is not a null pointer and the socket is not connection-oriented, the
     source address of the message is filled in.

实际代码:

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

希望这会有所帮助。

答案 6 :(得分:-3)

试试这个:

gethostbyname("localhost");