目标端口肯定是 19_999
。
要找出哪个IP地址接收当前数据包,您需要了解所有网络适配器的配置,包括虚拟适配器。然后,您可以从源IP和网络配置中获取目标IP,因为只有在同一网络中的设备才能相互通信。您只需在所有网络适配器中找到与源IP在同一网络中的IP即可。您可以通过将子网掩码应用于两个IP地址,并查看结果是否相同来判断两个设备是否在同一网络中。
顺便说一下,如果您的服务器在NAT后面,则无法获取客户端的真实源IP地址,如果它与服务器不在同一网络中,则只能获得最后一跳的源IP地址(通常是您的网络网关的IP地址)。
获取网络配置
{:ok, ifaddrs} = :inet.getifaddrs()
你会得到一个看起来像这样的列表。
[
{'lo', [addr: {127, 0, 0, 1}, netmask: {255, 0, 0, 0}, ...]},
{'eth0', [addr: {192, 168, 0, 10}, netmask: {255, 255, 255, 0}, ...]},
{'docker_gwbridge', [addr: {172, 18, 0, 1}, netmask: {255, 255, 0, 0}, ...]}
]
由于我们只需要addr
和netmask
,所以我们可以在列表上进一步进行Enum.map
:
ifaddrs = Enum.map(ifaddrs, fn{_, opts}-> {opts[:addr], opts[:netmask]} end)
应用网络掩码到IP地址上
将它们进行位运算与操作即可。
defp apply_netmask({a1, b1, c1, d1} = _ip, {a2, b2, c2, d2} = _netmask) do
{
a1 &&& a2,
b1 &&& b2,
c1 &&& c2,
d1 &&& d2
}
end
查找目标IP地址
dest_ip = ifaddrs
|> Enum.find(fn{addr, netmask}->
apply_netmask(addr, netmask) == apply_netmask(source_ip, netmask)
end)
|> elem(0)
更新
如果可能的话,建议您只在一台网络适配器上打开UDP套接字。您可以使用{ifaddr, inet:socket_address()}
选项来实现,例如:
{:ok, socket} = :gen_udp.open(19_999, [active: true, ifaddr: {192, 168, 0, 10}])
通过这种方式,您可以100%确定目标IP地址是192.168.0.10。此外,它还增加了一点安全性。
socket
模块似乎支持获取有关接收数据包的更多信息:http://erlang.org/doc/man/socket.html - legoscia:gen_udp.open
中设置的端口(在您的代码中为19_999)。然而,目标IP有点棘手,因为:gen_udp.open
默认侦听0.0.0.0
,这是指所有可用的网络适配器,包括像lo
这样的虚拟适配器。由于不同网络中的设备无法相互通信,因此您可以通过查看源IP来确定哪个IP地址正在接收当前数据包。 - Aetherus