文章目录
P49-P54:Address(socket01-06)
Address
模块就是把C标准版库中与socket
相关的地址API采用C++
面向对象的思想封装起来。
主要包含下列几个类:
- Address:基类,抽象类,对应
sockaddr
类型。提供获取地址的方法,以及一些基础操作的纯虚函数。 - IPAddress:继承
Address
,抽象类。提供关于IP
操作的纯虚函数。 - IPv4Address:继承
IPAddress
,对应sockaddr_in
类型。一个IPv4
地址。 - IPv6Address:继承
IPAddress
,对应sockaddr_in6
类型。一个IPv6
地址。 - UinxAddress:继承
Address
,Unix
域套接字类,对应sockaddr_un
类型。一个Unix
地址。 - UnknowAddress:继承
Address
,对应sockaddr
类型。未知地址。
一、地址结构体定义
sockaddr
/* 基础Socket地址 */
struct sockaddr
{
uint16 sa_family; //选择地址家族,AF_xxx | 例如是IPV4的就是选择AF_INET | AF_INET6
char sa_data[14]; // 地址数据 包含套接字中的目标地址和端口信息
};
sockaddr_in
/* 网络Socket地址 */
struct sockaddr_in
{
uint16 sin_family; // 地址家族: AF_INET
uint16 sin_port; // 两字节的端口号(网络字节顺序)
uint32 sin_addr.s_addr; // 网络地址IP4
unsigned char sin_zero[8]; /* Pad to size of `struct sockaddr'. */
};
sockaddr_in6
/* IPv6. */
struct sockaddr_in6
{
uint16 sin6_family; // 地址族
uint16 sin6_port; // 端口号
uint32 sin6_flowinfo; // 流信息
uint8 sin6_addr[16]; /* IPv6 地址 */
uint32 sin6_scope_id; /* IPv6 scope-id */
};
in6_addr
/* IPv6地址*/
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
#endif /*
二、地址API解释
getaddrinfo
根据域名获取IP地址信息
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo( const char *node,
const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
1)nodename:节点名可以是主机名,也可以是数字地址。(IPV4的10进点分,或是IPV6的16进制);
2)servname:包含十进制数的端口号或服务名如(ftp,http);
3)hints:是一个空指针或指向一个addrinfo结构的指针,由调用者填写关于它所想返回的信息类型的线索;
4)res:存放返回addrinfo结构链表的指针;
/* 释放addrinfo指针 */
void freeaddrinfo(struct addrinfo *res);
/* getaddrinfo函数返回的错误码转换为对应的错误信息字符串 */
const char *gai_strerror(int errcode);
struct addrinfo {
int ai_flags; // 位掩码,修改默认行为
int ai_family; // socket()的第一个参数
int ai_socktype; // socket()的第二个参数
int ai_protocol; // socket()的第三个参数
socklen_t ai_addrlen; // sizeof(ai_addr)
struct sockaddr *ai_addr; // sockaddr指针
char *ai_canonname; // 主机的规范名字
struct addrinfo *ai_next; // 链表的下一个结点
}
参数说明:
ai_flags:
1)AI_ADDRCONFIG: 只有当本地主机被配置为IPv4时,getaddrinfo返回IPv4地址,IPv6同理。
2)AI_CANONNAME: ai_canonname默认为NULL,设置此标志位,告诉getaddrinfo将列表中第一个addrinfo结构体的ai_cannoname字段指向host的权威(官方)名字。
3)AI_NUMERICSERV: service默认为服务名或端口号。这个标志强制参数service为端口号。
4)AI_PASSIVE: getaddrinfo默认返回套接字地址,客户端可以在调用connect时用作主动套接字。此标志位告诉该函数,返回的套接字地址可能被服务器用作监听套接字。此时,host应该为NULL。
5)AI_NUMERICHOST:用于指示getaddrinfo函数在解析主机名时是否进行名称解析。当我们需要使用IP地址而不是主机名来创建套接字时,可以将AI_NUMERICHOST常量作为getaddrinfo函数的hints参数中的ai_flags成员的值,以指示getaddrinfo函数不进行主机名解析,而直接使用传入的IP地址。这样可以避免主机名解析带来的延迟和不确定性,提高套接字的创建效率和可靠性。
getifaddrs
获取系统中所有网络接口的信息
#include <sys/types.h>
#include <ifaddrs.h>
/* getifaddrs创建一个链表,链表上的每个节点都是一个struct ifaddrs结构,getifaddrs()返回链表第一个元素的指针。
* 成功返回0, 失败返回-1,同时errno会被赋允相应错误码。 */
int getifaddrs(struct ifaddrs **ifap);
/* 释放ifaddrs */
void freeifaddrs(struct ifaddrs *ifa);
struct ifaddrs {
struct ifaddrs *ifa_next; /* 指向链表中下一个struct ifaddr结构 */
char *ifa_name; /* 网络接口名 */
unsigned int ifa_flags; /* 网络接口标志 */
struct sockaddr *ifa_addr; /* 指向一个包含网络地址的sockaddr结构 */
struct sockaddr *ifa_netmask; /* 指向一个包含网络掩码的结构 */
union {
struct sockaddr *ifu_broadaddr;
/* 如果(ifa_flags&IFF_BROADCAST)有效,ifu_broadaddr指向一个包含广播地址的结构 */
struct sockaddr *ifu_dstaddr;
/* 如果(ifa_flags&IFF_POINTOPOINT)有效,ifu_dstaddr指向一个包含p2p目的地址的结构 */
} ifa_ifu;
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
void *ifa_data; /* 指向一个缓冲区,其中包含地址族私有数据。没有私有数据则为NULL */
};
三、公共方法
createMask
根据前缀长度创建掩码
/*
* sizeof(T) * 8: 算出有多少位
* sizeof(T) * 8 - bits: 算出掩码0的个数
* 1 <<: 将 1 左移0的个数位
* -1: 前bits位数置为0,后面的 sizeof(T) * 8 - bits 位都为1
* ~: 将前bits位置为1,后面全部置为0
*/
template<class T>
static T createMask(uint32_t bits) {
return ~(1 << (sizeof(T) * 8 - bits)) - 1;
}
CountBytes
根据前缀长度常见掩码
template<class T>
static uint32_t CountBytes(T value) {
uint32_t result = 0;
for(; value; ++ result) {
// 将最右边的1置为0
value &= value - 1;
}
return result;
}
四、class Address
三个纯虚函数
// 获得sockaddr指针
virtual const sockaddr* getAddr() const = 0;
// 获得sockaddr长度
virtual socklen_t getAddrLen() const = 0;
// 可读性输出地址
virtual std::ostream& insert(std::ostream& os) const = 0;
Create
Address::ptr Address::Create(const sockaddr* addr, socklen_t addrlen) {
// 判断地址是否为空
if(addr == nullptr) {
return nullptr;
}
// 定义一个Address对象
Address::ptr result;
// 通过地址簇信息去初始化Address对象
switch(addr->sa_family) {
// IPv4
case AF_INET:
// 初始化一个IPv4地址
// 把addr强制转换成sockaddr_in
result.reset(new IPv4Address(*(const sockaddr_in*)addr));
break;
// IPv6
case AF_INET6:
// 初始化一个Ipv6地址
result.reset(new IPv6Address(*(const sockaddr_in6*)addr));
break;
// 未知类型
default:
result.reset(new UnknownAddress(*addr));
break;
}
// 返回创建的地址
return result;
}
Lookup
通过域名返回所有的Address(IPv4/v6)
/*
result : Address结果存储容器
host :域名地址
family : 地址簇 默认AF_UNSPEC(它通常用于指定地址族(Address Family)未指定的情况。在IPv4和IPv6地址族中,它表示任意地址族,即无论是IPv4还是IPv6地址都可以使用。)
type : socket的类型
protocol : 协议
*/
bool Address::Lookup(std::vector<Address::ptr>& result, const std::string& host,
int family, int type, int protocol) {
// 定义3个addrinfo结构体
// hints : 期望获取的地址信息的特征
// results : 结果容器
// next : 用来遍历results
addrinfo hints, *results, *next;
// 初始化hints
hints.ai_flags = 0;
hints.ai_family = family;
hints.ai_socktype = type;
hints.ai_protocol = protocol;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
// 主机名
std::string node;
// 端口号
const char* service = NULL;
// host = www.baidu.com:http / [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80
// 检查 ipv6地址:
if(!host.empty() && host[0] == '[') {
// 当 host = [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80就就会进入
// 去寻找第一个']'的位置,找到了返回该指针
// memchr在一段内存区域中搜索指定字符的第一次出现的位置
const char* endipv6 = (const char*)memchr(host.c_str() + 1, ']', host.size() - 1);
if(endipv6) {
// 查看endipv6下个位置是否为 : 确定是否有端口号
if(*(endipv6 + 1) == ':') {
// 跳过 ] 和 : 指向端口号的起始位置
service = endipv6 + 2;
}
// 去掉[] node
node = host.substr(1, endipv6 - host.c_str() - 1);
}
}
// 检查 ipv4地址 www.baidu.com:http
if(node.empty()) {
// 找到第一个 :
service = (const char*)memchr(host.c_str(), ':', host.size());
// 如果前面找到了一个:,判断后续是否还有:
if(service) {
// 如果没有:,表示当前:位置后面就是端口号
if(!memchr(service + 1, ':', host.c_str() + host.size() - service - 1)) {
node = host.substr(0, service - host.c_str());
++ service;
}
}
}
// 如果没设置端口号,就将host赋给他
if(node.empty()) {
node = host;
}
// 根据host获取IP地址,结果保存到results中
int error = getaddrinfo(node.c_str(), service, &hints, &results);
if(error) {
SYLAR_LOG_ERROR(g_logger) << "Address::Lookup getaddress(" << host << ","
<< family << ", " << type << ") err = " << error << " errstr = "
<< strerror(errno);
return false;
}
next = results;
// 遍历Address容器
while(next) {
// 每次迭代中,调用 Create 函数创建一个 Address 对象,并将其添加到 result 向量中。
result.push_back(Create(next->ai_addr, (socklen_t)next->ai_addrlen));
next = next->ai_next;
}
// 释放 getaddrinfo 函数中分配的内存,避免内存泄漏。
freeaddrinfo(results);
return true;
}
IPv6解析用例
// [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80
// 检查 ipv6address service [address]:
if(!host.empty() && host[0] == '[') {
// 当 host = [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80就就会进入
// 去寻找第一个']'的位置,找到了返回该指针
// memchr在一段内存区域中搜索指定字符的第一次出现的位置
const char* endipv6 = (const char*)memchr(host.c_str() + 1, ']', host.size() - 1);
// 找到了 ]
// [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80
// ^
// |
// endipv6
if(endipv6) {
// 查看endipv6下个位置是否为 : 确定是否有端口号
if(*(endipv6 + 1) == ':') {
// 跳过 ] 和 : 指向端口号的起始位置
service = endipv6 + 2;
// [2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80
// ^
// |
// service
}
// 去掉[] node = 2001:0db8:85a3:0000:0000:8a2e:0370:7334
node = host.substr(1, endipv6 - host.c_str() - 1);
}
}
IPv4解析用例
// 检查 ipv4地址 www.baidu.com:http
if(node.empty()) {
// 找到第一个 :
// www.baidu.com:http
// ^
// |
// service
service = (const char*)memchr(host.c_str(), ':', host.size());
// 如果前面找到了一个:,判断后续是否还有:
if(service) {
// 如果没有:,表示当前:位置后面就是端口号
if(!memchr(service + 1, ':', host.c_str() + host.size() - service - 1)) {
node = host.substr(0, service - host.c_str());
++ service;
}
// 结束后service指向端口号的起始位置
// www.baidu.com:http
// ^
// |
// service
}
}
LookupAny
Address::ptr Address::LookupAny(const std::string& host,int family, int type, int protocol) {
std::vector<Address::ptr> result;
// 调用Lookup方法
if(Lookup(result, host, family, type, protocol)) {
// 返回第一个Address结果
return result[0];
}
return nullptr;
}
LookupAnyIPAdress
IPAddress::ptr Address::LookupAnyIPAdress(const std::string& host,int family, int type, int protocol) {
std::vector<Address::ptr> result;
if(Lookup(result, host, family, type, protocol)) {
for(auto& i : result) {
// 和LookupAny差不多,多了一个转换步骤
IPAddress::ptr v = std::dynamic_pointer_cast<IPAddress>(i);
if(v) {
return v;
}
}
}
return nullptr;
}
GetInterfaceAddresses
/*
std::multimap<std::string, std::pair<Address::ptr, uint32_t>> : <网卡名,(地址信息,端口号)>
*/
bool Address::GetInterfaceAddresses(std::multimap<std::string, std::pair<Address::ptr, uint32_t>>& result, int family) {
// 定义两个ifaddrs类型的对象,next用于遍历,results用于存储结果
struct ifaddrs *next, *results;
// getifaddrs创建一个链表,链表上的每个节点都是一个struct ifaddrs结构,getifaddrs()返回链表第一个元素的指针
if(getifaddrs(&results) != 0) {
SYLAR_LOG_ERROR(g_logger) << "Address::GetInterfaceAddresses getifaddrs "
<< " err = " << errno << " errstr = " << strerror(errno);
return false;
}
try {
// results指向头节点,用next遍历
for(next = results; next; next = next->ifa_next) {
Address::ptr addr;
uint32_t prefix_length = ~0u;
// 检查当前接口的地址簇是否符合要求
if(family != AF_UNSPEC && family != next->ifa_addr->sa_family) {
continue;
}
// 分别处理IPv4/6地址
switch(next->ifa_addr->sa_family) {
// IPv4
case AF_INET:
{
// 创建ipv4地址
addr = Create(next->ifa_addr, sizeof(sockaddr_in));
// 获取掩码地址
uint32_t netmask = ((sockaddr_in*)next->ifa_netmask)->sin_addr.s_addr;
// 获取前缀长度,网络地址的长度
prefix_length = CountBytes(netmask);
}
break;
// IPv6
case AF_INET6:
{
// 创建ipv6地址
addr = Create(next->ifa_addr, sizeof(sockaddr_in6));
// 计算掩码
in6_addr& netmask = ((sockaddr_in6*)next->ifa_netmask)->sin6_addr;
// 在IPv6地址的计算中,每个字节(共16个字节)都有可能包含子网掩码的部分信息,所以要累计计算
prefix_length = 0;
for(int i = 0; i < 16; ++ i) {
prefix_length += CountBytes(netmask.s6_addr[i]);
}
}
default:
break;
}
// 如果成功创建了地址,保存<网卡名,地址和前缀长度>到结果容器中
if(addr) {
result.insert(std::make_pair(next->ifa_name, std::make_pair(addr, prefix_length)));
}
}
} catch(...) {
SYLAR_LOG_ERROR(g_logger) << "Address::GetInterfaceAddresses exception";
freeifaddrs(results);
return false;
}
freeifaddrs(results);
return true;
}
例子
- eth0
- IPv4地址:192.168.1.100,子网掩码:255.255.255.0
- IPv6地址:fe80::1,子网掩码:ffff:ffff:ffff::
- eth1
- IPv4地址:10.0.0.2,子网掩码:255.255.255.0
- IPv6地址:fe80::2,子网掩码:ffff:ffff:ffff::
根据family选择性地获取IPv4或IPv6地址,并将每个接口的名称、地址对象和子网掩码前缀长度存储在 result
容器中
std::multimap<std::string, std::pair<Address::ptr, uint32_t>> result;
Address::GetInterfaceAddresses(result, AF_INET);
// 此时 result 中存储的内容如下:
// eth0: <IPv4地址 192.168.1.100, 前缀长度 24>
// eth1: <IPv4地址 10.0.0.2, 前缀长度 24>
Address::GetInterfaceAddresses(result, AF_INET6);
// 此时 result 中存储的内容如下:
// eth0: <IPv6地址 fe80::1, 前缀长度 64>
// eth1: <IPv6地址 fe80::2, 前缀长度 64>
GetInterfaceAddresses2
bool Address::GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t>>& result, const std::string& iface, int family) {
// 如果传入的接口名称为空或者为通配符*,表示获取所有接口的地址信息
if(iface.empty() || iface == "*") {
// 然后根据参数 family 的值,选择性地获取IPv4或IPv6地址
if(family == AF_INET || family == AF_UNSPEC) {
result.push_back(std::make_pair(Address::ptr(new IPv4Address()), 0u));
}
if(family == AF_INET6 || family == AF_UNSPEC) {
result.push_back(std::make_pair(Address::ptr(new IPv6Address()), 0u));
}
return true;
}
// 定义一个结果容器
std::multimap<std::string, std::pair<Address::ptr, uint32_t>> results;
// 获取失败
if(!GetInterfaceAddresses(results, family)) {
return false;
}
// 返回pair,first为第一个等于的迭代器,second为第一个大于的迭代器
// 找到指定接口
auto its = results.equal_range(iface);
for(; its.first != its.second; ++ its.first) {
// its.first->second等于std::pair<Address::ptr, uint32_t>
result.push_back(its.first->second);
}
return true;
}
例子
std::multimap<std::string, std::pair<Address::ptr, uint32_t>> results;
// results 中存储的内容如下:
// "eth0": <IPv4地址 192.168.1.100, 前缀长度 24>
// "eth0": <IPv6地址 fe80::1, 前缀长度 64>
// "eth1": <IPv4地址 10.0.0.2, 前缀长度 24>
// "eth1": <IPv6地址 fe80::2, 前缀长度 64>
auto its = results.equal_range("eth0");
// its.first 指向 "eth0" 的第一个匹配项
// its.second 指向 "eth1" 的第一个匹配项
for(; its.first != its.second; ++ its.first) {
// 第一次循环,将 "eth0" 对应的两个地址信息放入 result
result.push_back(its.first->second);
}
// result 中存储的内容如下:
// <IPv4地址 192.168.1.100, 前缀长度 24>
// <IPv6地址 fe80::1, 前缀长度 64>
class IPAddress
成员函数
typedef std::shared_ptr<IPAddress> ptr;
// 通过IP、域名、服务器名,端口号创建IPAddress
static IPAddress::ptr Create(const char* address, uint32_t port = 0);
// 获得广播地址
virtual IPAddress::ptr broadcastAddress(uint32_t prefix_len) = 0;
// 获得网络地址
virtual IPAddress::ptr networkAddress(uint32_t prefix_len) = 0;
// 获得子网掩码地址
virtual IPAddress::ptr subnetMask(uint32_t prefix_len) = 0;
// 获得端口号
virtual uint32_t getPort() const = 0;
// 设置端口号
virtual void setPPort(uint32_t v) = 0;
Create
/// IPAddress
IPAddress::ptr IPAddress::Create(const char* address, uint32_t port) {
addrinfo hints, *results;
memset(&hints, 0, sizeof(addrinfo));
// hints.ai_flags = AI_NUMERICHOST;
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
int error = getaddrinfo(address, NULL, &hints, &results);
if(error) {
SYLAR_LOG_ERROR(g_logger) << "IPAddress::Create(" << address << ", "
<< port << ") error = " << errno << " errno = " << errno
<< " errstr = " << strerror(errno);
return nullptr;
}
try {
IPAddress::ptr result = std::dynamic_pointer_cast<IPAddress>(Address::Create(results->ai_addr, (socklen_t)results->ai_addrlen));
if(result) {
result->setPPort(port);
}
freeaddrinfo(results);
return result;
} catch (...) {
freeaddrinfo(results);
return nullptr;
}
}
class IPv4Address
成员变量
// Socket结构体
sockaddr_in m_addr;
Create
IPv4Address::ptr IPv4Address::Create(const char* address, uint32_t port) {
IPv4Address::ptr rt(new IPv4Address);
rt->m_addr.sin_port = byteswapOnLittileEndian(port);
// 将一个IP地址的字符串表示转换为网络字节序的二进制形式
int result = inet_pton(AF_INET, address, &rt->m_addr.sin_addr);
if(result <= 0) {
SYLAR_LOG_ERROR(g_logger) << "IPv4Address::Create(" << address << ", "
<< port << ") rt = " << result << " errno = " << errno
<< " errstr = " << strerror(errno);
return nullptr;
}
return rt;
}
构造函数
IPv4Address::IPv4Address(const sockaddr_in& address) {
m_addr = address;
}
IPv4Address::IPv4Address(uint32_t address, uint32_t port) {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sin_family = AF_INET;
m_addr.sin_port = byteswapOnLittileEndian(port);
m_addr.sin_addr.s_addr = byteswapOnLittileEndian(address);
}
broadcastAddress
IPAddress::ptr IPv4Address::broadcastAddress(uint32_t prefix_len) {
if(prefix_len > 32) {
return nullptr;
}
sockaddr_in baddr(m_addr);
// 主机号全为1
baddr.sin_addr.s_addr |= byteswapOnLittileEndian(createMask<uint32_t>(prefix_len));
return IPv4Address::ptr(new IPv4Address(baddr));
}
networkAddress
IPAddress::ptr IPv4Address::networkAddress(uint32_t prefix_len) {
if(prefix_len > 32) {
return nullptr;
}
sockaddr_in baddr(m_addr);
// 主机号全为0
baddr.sin_addr.s_addr &= byteswapOnLittileEndian(createMask<uint32_t>(prefix_len));
return IPv4Address::ptr(new IPv4Address(baddr));
}
subnetMask
IPAddress::ptr IPv4Address::subnetMask(uint32_t prefix_len) {
sockaddr_in subnet;
memset(&subnet, 0, sizeof(subnet));
subnet.sin_family = AF_INET;
// 根据前缀长度获得子网掩码
subnet.sin_addr.s_addr = ~byteswapOnLittileEndian(createMask<uint32_t>(prefix_len));
return IPv4Address::ptr(new IPv4Address(subnet));
}
class IPv6Address
成员变量
sockaddr_in6 m_addr;
Create
IPv6Address::ptr IPv6Address::Create(const char* address, uint32_t port) {
IPv6Address::ptr rt(new IPv6Address);
rt->m_addr.sin6_port = byteswapOnLittileEndian(port);
int result = inet_pton(AF_INET6, address, &rt->m_addr.sin6_addr);
if(result <= 0) {
SYLAR_LOG_ERROR(g_logger) << "IPv6Address::Create(" << address << ", "
<< port << ") rt = " << result << " errno = " << errno
<< " errstr = " << strerror(errno);
return nullptr;
}
return rt;
}
构造函数
// 默认构造函数
IPv6Address::IPv6Address() {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sin6_family = AF_INET6;
}
// 通过sockaddr_in6
IPv6Address::IPv6Address(const sockaddr_in6& sockaddr) {
m_addr = sockaddr;
}
// 通过address和port
IPv6Address::IPv6Address(const uint8_t address[16], uint16_t port) {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sin6_family = AF_INET6;
m_addr.sin6_port = byteswapOnLittleEndian(port);
memcpy(&m_addr.sin6_addr.__in6_u.__u6_addr8, address, 16);
}
insert
std::ostream& IPv6Address::insert(std::ostream& os) const {
os << "[";
// m_addr.sin6_addr.s6_addr 是一个 unsigned char[16] 数组,存储了 IPv6 地址的二进制表示。
uint16_t* addr = (uint16_t*)m_addr.sin6_addr.s6_addr;
// 标记是否已经输出过连续的零段
bool used_zeros = false;
// 遍历 IPv6 地址的每个 16 比特段(共 8 个段)
for(size_t i = 0; i < 8; ++ i) {
// 如果当前段的值为 0,且之前未输出过连续的零段,则跳过当前段的输出。
if(addr[i] == 0 && !used_zeros) {
continue;
}
// 如果当前段不是第一个段且前一个段的值为 0,且之前未输出过连续的零段,则输出一个冒号 :,表示连续的零段已经开始。
if(i && addr[i - 1] == 0 && !used_zeros) {
os << ":";
used_zeros = true;
}
// 如果当前段不是第一个段,则输出一个冒号 :,用于分隔每个段。
if(i) {
os << ':';
}
os << std::hex << (int)byteswapOnLittileEndian(addr[i]) << std::dec;
}
// 如果未输出过连续的零段且最后一个段的值为 0,则输出双冒号 ::,表示连续的零段结束。
if(!used_zeros && addr[7] ==0 ) {
os << "::";
}
os << "]:" << byteswapOnLittileEndian(m_addr.sin6_port);
return os;
}
broadcastAddress
IPAddress::ptr IPv6Address::broadcastAddress(uint32_t prefix_len) {
sockaddr_in6 baddr(m_addr);
baddr.sin6_addr.s6_addr[prefix_len / 8] |= createMask<uint8_t>(prefix_len % 8);
for(int i = prefix_len / 8 + 1; i < 16; ++ i) {
baddr.sin6_addr.s6_addr[i] = 0xff;
}
return IPv6Address::ptr(new IPv6Address(baddr));
}
networkAddress
IPAddress::ptr IPv6Address::networkAddress(uint32_t prefix_len) {
sockaddr_in6 naddr(m_addr);
/* 找到前缀长度结尾在第几个字节,在该字节在哪个位置。
* 将该字节前剩余位置全部置为0 */
naddr.sin6_addr.__in6_u.__u6_addr8[prefix_len / 8] &=
createMask<uint8_t>(prefix_len % 8);
// 将后面其余字节置为0
for (int i = prefix_len / 8 + 1; i < 16; ++i) {
naddr.sin6_addr.__in6_u.__u6_addr8[i] = 0x00;
}
return IPv6Address::ptr(new IPv6Address(naddr));
}
subnetMask
IPAddress::ptr IPv6Address::subnetMask(uint32_t prefix_len) {
sockaddr_in6 subnet;
memset(&subnet, 0, sizeof(subnet));
subnet.sin6_family = AF_INET6;
subnet.sin6_addr.s6_addr[prefix_len / 8] = ~createMask<uint8_t>(prefix_len % 8);
for(uint32_t i = 0; i < prefix_len / 8; ++ i) {
subnet.sin6_addr.s6_addr[i] = 0xFF;
}
return IPv6Address::ptr(new IPv6Address(subnet));
}
class UnixAddress
成员变量
// 结构体
struct sockaddr_un m_addr;
// 长度
socklen_t m_length;
构造函数
UnixAddress::UnixAddress() {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sun_family = AF_UNIX;
// sun_path的偏移量+最大路径
m_length = offsetof(sockaddr_un, sun_path) + MAX_PATH_LEN;
}
UnixAddress::UnixAddress(const std::string& path) {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sun_family = AF_UNIX;
// 加上'\0'的长度
m_length = path.size() + 1;
if(!path.empty() && path[0] == '\0') {
-- m_length;
}
if(m_length <= sizeof(m_addr.sun_path)) {
throw std::logic_error("path too long");
}
// 将path放入结构体
memcpy(m_addr.sun_path, path.c_str(), m_length);
// 偏移量+路径长度
m_length += offsetof(sockaddr_un, sun_path);
}
五、测试
域名转IP地址
void test() {
// 域名转IP地址
std::vector<sylar::Address::ptr> addrs;
bool v = sylar::Address::Lookup(addrs, "www.baidu.com");
if(!v) {
SYLAR_LOG_ERROR(g_logger) << "lookup fail";
return;
}
for(size_t i = 0; i < addrs.size(); ++ i) {
SYLAR_LOG_INFO(g_logger) << i << " - " << addrs[i]->toString();
}
}
获得本机网卡地址
// 测试网卡
std::multimap<std::string, std::pair<sylar::Address::ptr, uint32_t>> results;
bool v = sylar::Address::GetInterfaceAddresses(results);
if(!v) {
SYLAR_LOG_ERROR(g_logger) << "GetInterfaceAddresses fail";
return;
}
for(auto& i : results) {
SYLAR_LOG_INFO(g_logger) << i.first << " - " << i.second.first->toString() << " - " << i.second.second;
}
创建IPv4地址
auto addr = sylar::IPv4Address::Create("112.80.248.75", 80);
auto saddr = addr->subnetMask(24);
auto baddr = addr->broadcastAddress(24);
auto naddr = addr->networkAddress(24);
if (addr) {
SYLAR_LOG_INFO(g_logger) << addr->toString();
}
if (saddr) {
SYLAR_LOG_INFO(g_logger) << saddr->toString();
}
if (baddr) {
SYLAR_LOG_INFO(g_logger) << baddr->toString();
}
if (naddr) {
SYLAR_LOG_INFO(g_logger) << naddr->toString();
}
创建IPv6地址
auto addr = sylar::IPv6Address::Create("fe80::215:5dff:fe20:e26a", 8020);
if (addr) {
SYLAR_LOG_INFO(g_logger) << addr->toString();
}
auto saddr = addr->subnetMask(64);
auto baddr = addr->broadcastAddress(64);
auto naddr = addr->networkAddress(64);
if (addr) {
SYLAR_LOG_INFO(g_logger) << addr->toString();
}
if (saddr) {
SYLAR_LOG_INFO(g_logger) << saddr->toString();
}
if (baddr) {
SYLAR_LOG_INFO(g_logger) << baddr->toString();
}
if (naddr) {
SYLAR_LOG_INFO(g_logger) << naddr->toString();
}