我知道有关广播地址的问题已经讨论过很多次了。但我没有找到任何答案,在计算广播地址时考虑了网络掩码前缀长度。 所有答案都给出了类似 x.x.x.255 的代码,并且都很高兴,但是,正如我们从这里知道的那样 https://www.wikihow.com/Calculate-Network-and-Broadcast-Address,255 并不总是正确的。 F.E. 广播必须使用网络掩码前缀长度来计算
那么如果使用 boost asio 我会做什么:
使用 tcp::resolver::query 获取所有本地接口的列表
循环 tcp::resolver::iterator
2.1 - extract IP from endpoint
2.2 - need to do something, to determine REAL broadcast address of the network the interface connected to!
std::set<std::string> interfaces;
using boost::asio::ip::tcp;
boost::asio::io_service service;
boost::system::error_code ec;
tcp::resolver::query resolver_query(tcp::v4(), hostname(), "", tcp::resolver::query::numeric_service);
tcp::resolver resolver(service);
tcp::resolver::iterator it = resolver.resolve(resolver_query, ec);
if (ec)
{
//std::cout << "Failed to resolve a DNS name." << "Error code = " << ec.value() << ". Message = " << ec.message();
return interfaces;
}
tcp::resolver::iterator it_end;
typedef boost::asio::ip::address_v4 A;
// loop over iterator
for (; it != it_end; ++it)
{
auto addr = it->endpoint().address();
// case A: returns something like x.x.x.255
auto broadcast = A::broadcast(addr, A::netmask(addr))
//case B: returns always 255.255.255.255
//auto broadcast = A::netmask(add).broadcast().to_str();
interfaces.emplace(broadcast.to_string());
}
上面的代码仅适用于情况 A,并且仅当接口(循环中的端点)的 IP 地址为 x.x.x.x/24 时才有效。 在前缀长度为 f.e 的情况下/25,计算出的广播地址错误(仍然以255结尾),发送给它的广播消息没有到达节点。
我知道boost asio中有一个类network_v4,它有prefix_length() getter,但是要创建network_v4的实例,我需要将已知的前缀长度传递到构造函数中,所以这看起来就像是先有蛋还是先有鸡的困境。
有没有办法使用每个本地接口所连接的网络的前缀长度来获取正确的广播地址?除了端点及其地址之外,解析器似乎无法为我提供更多内容。
UPD。经过一些研究,我发现 boost::asio 没有为与 /24 不同的子网提供正确的地址。
以下针对 Windows 使用 GetIpAddrTable() 和 Linux 使用 getifaddrs() 的解决方案在具有不同前缀的本地网络中进行了检查。
std::string bipaddress(const std::string& interface)
{
std::string result{ "" };
#ifdef WIN32
auto inet_n2a = [](uint32_t addr, char* ipbuf)
{
sprintf(ipbuf, "%li.%li.%li.%li", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF);
};
PMIB_IPADDRTABLE p_ip_table;
DWORD dw_size = 0;
p_ip_table = (MIB_IPADDRTABLE*)malloc(sizeof(MIB_IPADDRTABLE));
if (p_ip_table)
{
if (GetIpAddrTable(p_ip_table, &dw_size, 0) == ERROR_INSUFFICIENT_BUFFER)
{
free(p_ip_table);
p_ip_table = (MIB_IPADDRTABLE*)malloc(dw_size);
if (p_ip_table == NULL)
{
return result;
}
}
}
else
return result;
DWORD dw_ret_val = 0;
LPVOID lp_msg_buf = nullptr;
if ((dw_ret_val = GetIpAddrTable(p_ip_table, &dw_size, 0)) != NO_ERROR)
{
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw_ret_val,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lp_msg_buf, 0, NULL))
{
LocalFree(lp_msg_buf);
}
return result;
}
IN_ADDR ip_addr{};
IN_ADDR mask_addr{};
for (int i = 0; i < (int)p_ip_table->dwNumEntries; i++)
{
ip_addr.S_un.S_addr = ntohl((u_long)p_ip_table->table[i].dwAddr);
char ifa_addr_str[INET_ADDRSTRLEN]{ 0 };
inet_n2a(ip_addr.S_un.S_addr, ifa_addr_str);
mask_addr.S_un.S_addr = ntohl((u_long)p_ip_table->table[i].dwMask);
uint32_t baddr = ip_addr.S_un.S_addr & mask_addr.S_un.S_addr;
if (p_ip_table->table[i].dwBCastAddr)
baddr |= ~mask_addr.S_un.S_addr;
char dst_addr_str[INET_ADDRSTRLEN]{ 0 };
inet_n2a(baddr, dst_addr_str);
if (ifa_addr_str == interface)
{
result = dst_addr_str;
break;
}
}
if (p_ip_table)
{
free(p_ip_table);
p_ip_table = nullptr;
}
#else
struct ifaddrs* ifas;
if (getifaddrs(&ifas) == -1)
{
return "";
}
struct ifaddrs* ifa = ifas;
while (ifa)
{
int family = ifa->ifa_addr->sa_family;
if (family == AF_INET)
{
char ap[100]{ 0 };
const int family_size = family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
getnameinfo(ifa->ifa_addr, family_size, ap, sizeof(ap), 0, 0, NI_NUMERICHOST);
if (interface == ap)
{
unsigned int tmp_mask = ((struct sockaddr_in *)(ifa->ifa_netmask))->sin_addr.s_addr;
unsigned int tmp_addr = ((struct sockaddr_in *)(ifa->ifa_addr))->sin_addr.s_addr;
unsigned int dst = tmp_addr & tmp_mask;
dst |= ~ tmp_mask;
char dst_buffer[INET_ADDRSTRLEN]{0};
inet_ntop(AF_INET, &dst, dst_buffer, INET_ADDRSTRLEN);
result = dst_buffer;
break;
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifas);
#endif
return result;
}