sylar高性能服务器-日志(P49-P53)内容记录

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:继承AddressUnix域套接字类,对应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应该为NULL5)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;
}

例子

  1. eth0
    • IPv4地址:192.168.1.100,子网掩码:255.255.255.0
    • IPv6地址:fe80::1,子网掩码:ffff:ffff:ffff::
  2. 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();
    }
}

image-20240304092124472

获得本机网卡地址
// 测试网卡
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;
}

image-20240304092225526

创建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();
    }

image-20240304092429277

创建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();
    }

image-20240304092600637

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

madkeyboard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值