记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会
项目仓库地址
https://github.com/sylar-yin/sylar/
整理博客过程中参考的大佬资料链接:
============================================
基础介绍
封装了各种Address类,提供方便的操作接口,支持IPv4,IPv6,UnixAddress
所有类都派生自基类Address
Address类
作为后续所有地址类的基类,提供域名解析(使用getaddrinfo()),获取网卡配置,获取sockaddr,获取协议族,字符串化等方法
// 所有Address类的抽象基类
class Address {
public:
typedef std::shared_ptr
<Address> ptr;
static Address::ptr Create(const sockaddr* addr, socklen_t addrlen); // 通过sockaddr创建地址
static bool Lookup(std::vector<Address::ptr>& result, const std::string& host,
int family = AF_UNSPEC, int type = 0, int protocol = 0); // 通过host返回所有Address
static Address::ptr LookupAny(const std::string& host,
int family = AF_UNSPEC, int type = 0, int protocol = 0); // 通过host返任意Address
static std::shared_ptr
<IPAddress> LookupAnyIPAddress(const std::string& host,
int family = AF_UNSPEC, int type = 0, int protocol = 0); // 通过host返回任意IPAddress
// 获取本机所有网卡配置
static bool GetInterfaceAddresses(std::multimap<std::string
, std::pair<Address::ptr, uint32_t>>& result,
int family = AF_UNSPEC);
// 获取指定网卡配置
static bool GetInterfaceAddresses(std::vector<std::pair<Address::ptr, uint32_t>>& result
, const std::string& iface, int family = AF_UNSPEC);
virtual ~Address() {}
int getFamily() const; // 返回地址协议族
virtual const sockaddr* getAddr() const = 0; // 获取sockaddr指针
virtual sockaddr* getAddr() = 0; // 获取sockaddr指针(非const版本)
virtual socklen_t getAddrLen() const = 0; // 获取sockaddr长度(socklen_t类型)
virtual std::ostream& insert(std::ostream& os) const = 0; // 将地址转成字符串插入到流
std::string toString() const; // 返回字符串格式的地址
bool operator<(const Address& rhs) const;
bool operator==(const Address& rhs) const;
bool operator!=(const Address& rhs) const;
};
Address::ptr Address::Create(const sockaddr* addr, socklen_t addrlen) {
if(addr == nullptr) {
return nullptr;
}
Address::ptr result;
// 根据地址协议族创建地址
switch(addr->sa_family) {
case AF_INET:
result.reset(new IPv4Address(*(const sockaddr_in*)addr));
break;
case AF_INET6:
result.reset(new IPv6Address(*(const sockaddr_in6*)addr));
break;
default:
result.reset(new UnknownAddress(*addr));
break;
}
return result;
}
bool Address::Lookup(std::vector<Address::ptr>& result, const std::string& host,
int family, int type, int protocol) {
addrinfo hints, *results, *next;
hints.ai_flags = 0;
hints.ai_family = family;
hints.ai_socktype = type;
hints.ai_protocol = protocol;
hints.ai_addrlen = 0;
hints.ai_addr = NULL;
hints.ai_canonname = NULL;
hints.ai_next = NULL;
std::string node;
const char* service = NULL;
// 解析字符串地址(设置node和service的值,用于getaddrinfo函数进行DNS查询)
// 检查 IPv6address service
if(!host.empty() && host[0] == '[') {
const char* endipv6 = (const char*)memchr(host.c_str() + 1, ']', host.size() - 1);
if(endipv6) {
// todo check out of range
if(*(endipv6 + 1) == ':') {
service = endipv6 + 2;
}
node = host.substr(1, endipv6 - host.c_str() - 1);
}
}
// 检查 node service
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++;
}
}
}
if(node.empty()) {
node = host;
}
// 使用getaddrinfo(),进行DNS查询,返回地址链表(addrinfo链表)
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;
}
// 将地址链表遍历,构造Address对象存入result容器
next = results;
while(next) {
result.push_back(Create(next->ai_addr, (socklen_t)next->ai_addrlen));
next = next->ai_next;
}
freeaddrinfo(results); // 释放addrinfo指针
return !result.empty();
}
Address::ptr Address::LookupAny(const std::string& host,
int family, int type, int protocol) {
std::vector<Address::ptr> result;
if(Lookup(result, host, family, type, protocol)) {
return result[0]; // 返回链表的第一个元素构造的地址
}
return nullptr;
}
IPAddress类
派生自Address,仍然为抽象类,同时将作为IPv4Address和IPv6Address的基类,相比Address类新增了Create方法以及获取广播地址,网络段地址和子网掩码地址的虚方法,等待子类各自实现
// 继承自Address的抽象类
class IPAddress : public Address {
public:
typedef std::shared_ptr
<IPAddress> ptr;
static IPAddress::ptr Create(const char* address, uint16_t port = 0); // 创建IPAddress
virtual IPAddress::ptr broadcastAddress(uint32_t prefix_len) = 0; // 获取广播地址
virtual IPAddress::ptr networkAddress(uint32_t prefix_len) = 0; // 获取网络地址
virtual IPAddress::ptr subnetAddress(uint32_t prefix_len) = 0; // 获取子网掩码地址
virtual uint32_t getPort() const = 0; // 获取端口号
virtual void setPort(uint16_t v) = 0; // 设置端口号
};
IPAddress::ptr IPAddress::Create(const char* address, uint16_t port) {
addrinfo hints, *results;
memset(&hints, 0, sizeof(addrinfo));
hints.ai_family = AF_UNSPEC;
// 不管是不是纯IP或域名都解析一下,然后使用链表第一个结果
int error = getaddrinfo(address, NULL, &hints, &results);
if(error) {
SYLAR_LOG_ERROR(g_logger) << "IPAddress::Create(" << address
<< ", " << port << " error=" << error
<< " errno=" << errno
<< " errstr=" << strerror(errno);
return nullptr;
}
try {
// 根据返回的链表的第一个结果创建Address,并转换成IPAddress格式
IPAddress::ptr result = std::dynamic_pointer_cast
<IPAddress>(
Address::Create(results->ai_addr, (socklen_t)results->ai_addrlen));
if(result) {
// 设置端口
result->setPort(port);
}
freeaddrinfo(results);
return result;
} catch (...) {
freeaddrinfo(results);
return nullptr;
}
}
IPv4Address
继承自IPAddress,实现了基类的虚方法,成员变量存储了一个sockaddr_in结构体
// 继承自IPAddress类,表示一个IPv4地址
class IPv4Address : public IPAddress {
public:
typedef std::shared_ptr
<IPv4Address> ptr;
static IPv4Address::ptr Create(const char* address, uint16_t port = 0); // 创建IPv4Address
IPv4Address(const sockaddr_in& address); // 构造函数(通过sockaddr_in)
IPv4Address(uint32_t address = INADDR_ANY, uint32_t port = 0); // 构造函数(通过IP地址和端口号)
const sockaddr* getAddr() const override; // 获取sockaddr指针
sockaddr* getAddr() override; // 获取sockaddr指针(非const版本)
socklen_t getAddrLen() const override; // 获取sockaddr长度(socklen_t类型)
std::ostream& insert(std::ostream& os) const override; // 将地址转成字符串插入到流
IPAddress::ptr broadcastAddress(uint32_t prefix_len) override; // 获取广播地址
IPAddress::ptr networkAddress(uint32_t prefix_len) override; // 获取网络地址
IPAddress::ptr subnetAddress(uint32_t prefix_len) override; // 获取子网掩码地址
uint32_t getPort() const override; // 获取端口号
void setPort(uint16_t v) override; // 设置端口号
private:
sockaddr_in m_addr; //sockaddr_in 结构体,存放IPv4地址
};
// IPv4
IPv4Address::ptr IPv4Address::Create(const char* address, uint16_t port) {
// 创建一个IPv4Address对象
IPv4Address::ptr rt(new IPv4Address);
// 设置端口(转成网络字节序)
rt->m_addr.sin_port = byteswapOnLittleEndian(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 << " erron=" << 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 = byteswapOnLittleEndian(port);
m_addr.sin_addr.s_addr = byteswapOnLittleEndian(address);
}
sockaddr* IPv4Address::getAddr() {
return (sockaddr*)&m_addr;
}
const sockaddr* IPv4Address::getAddr() const {
return (sockaddr*)&m_addr;
}
socklen_t IPv4Address::getAddrLen() const {
return sizeof(m_addr);
}
std::ostream& IPv4Address::insert(std::ostream& os) const {
uint32_t addr = byteswapOnLittleEndian(m_addr.sin_addr.s_addr);
// 转成十进制数字 + '.' 分割的格式,输出到流
os << ((addr >> 24) & 0xff) << "."
<< ((addr >> 16) & 0xff) << "."
<< ((addr >> 8) & 0xff) << "."
<< (addr & 0xff);
os << ":" << byteswapOnLittleEndian(m_addr.sin_port);
return os;
}
// 获取广播地址
IPAddress::ptr IPv4Address::broadcastAddress(uint32_t prefix_len) {
if(prefix_len > 32) {
return nullptr;
}
sockaddr_in baddr(m_addr);
baddr.sin_addr.s_addr |= byteswapOnLittleEndian(
CreateMask
<uint32_t>(prefix_len)
);
return IPv4Address::ptr(new IPv4Address(baddr));
}
// 获取网络地址
IPAddress::ptr IPv4Address::networkAddress(uint32_t prefix_len) {
if(prefix_len > 32) {
return nullptr;
}
sockaddr_in baddr(m_addr);
baddr.sin_addr.s_addr &= ~byteswapOnLittleEndian(
CreateMask
<uint32_t>(prefix_len)
);
return IPv4Address::ptr(new IPv4Address(baddr));
}
// 获取子网掩码
IPAddress::ptr IPv4Address::subnetAddress(uint32_t prefix_len) {
sockaddr_in subnet;
memset(&subnet, 0, sizeof(subnet));
subnet.sin_family = AF_INET;
subnet.sin_addr.s_addr = ~byteswapOnLittleEndian(CreateMask
<uint32_t>(prefix_len));
return IPv4Address::ptr(new IPv4Address(subnet));
}
uint32_t IPv4Address::getPort() const {
return byteswapOnLittleEndian(m_addr.sin_port);
}
void IPv4Address::setPort(uint16_t v) {
m_addr.sin_port = byteswapOnLittleEndian(v);
}
IPv6Address
继承自IPAddress,实现了基类的虚方法,成员变量存储了一个sockaddr_in6结构体
注意IPv6其实没有像IPv4那样的广播地址,sylar为了保证接口一致性就没有删除这些方法
// 继承自IPAddress类,表示一个IPv6地址
class IPv6Address : public IPAddress {
public:
typedef std::shared_ptr
<IPv6Address> ptr;
static IPv6Address::ptr Create(const char* address, uint16_t port = 0); // 创建IPv6Address
IPv6Address(); // 构造函数(默认)
IPv6Address(const sockaddr_in6& address); // 构造函数(通过sockaddr_in6)
IPv6Address(const uint8_t address[16], uint16_t port = 0); // 构造函数(通过地址和端口)
const sockaddr* getAddr() const override; // 获取sockaddr指针
sockaddr* getAddr() override; // 获取sockaddr指针(非const版本)
socklen_t getAddrLen() const override; // 获取sockaddr长度(socklen_t类型)
std::ostream& insert(std::ostream& os) const override; // 将地址转成字符串插入到流
IPAddress::ptr broadcastAddress(uint32_t prefix_len) override; // 获取广播地址
IPAddress::ptr networkAddress(uint32_t prefix_len) override; // 获取网络地址
IPAddress::ptr subnetAddress(uint32_t prefix_len) override; // 获取子网掩码地址
uint32_t getPort() const override; // 获取端口号
void setPort(uint16_t v) override; // 设置端口号
private:
sockaddr_in6 m_addr; // sockaddr_in6 结构体,存放IPv6地址
};
IPv6Address::ptr IPv6Address::Create(const char* address, uint16_t port) {
IPv6Address::ptr rt(new IPv6Address);
// 设置端口号
rt->m_addr.sin6_port = byteswapOnLittleEndian(port);
// 将字符串表示的IP地址转换成网络字节序的二进制表示
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 << " erron=" << errno
<< " errstr=" << strerror(errno);
return nullptr;
}
return rt;
}
IPv6Address::IPv6Address() {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sin6_family = AF_INET6;
}
IPv6Address::IPv6Address(const sockaddr_in6& address) {
m_addr = address;
}
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.s6_addr, address, 16);
}
sockaddr* IPv6Address::getAddr() {
return (sockaddr*)&m_addr;
}
const sockaddr* IPv6Address::getAddr() const {
return (sockaddr*)&m_addr;
}
socklen_t IPv6Address::getAddrLen() const {
return sizeof(m_addr);
}
std::ostream& IPv6Address::insert(std::ostream& os) const {
os << "[";
uint16_t* addr = (uint16_t*)m_addr.sin6_addr.s6_addr;
bool used_zeros = false;
for(size_t i = 0; i < 8; i++) {
if(addr[i] == 0 && !used_zeros) {
continue;
}
if(i && addr[i - 1] == 0 && !used_zeros) {
os << ":";
used_zeros = true;
}
if(i) {
os << ":";
}
os << std::hex << (int)byteswapOnLittleEndian(addr[i]) << std::dec;
}
// 若最后一块为0则省略
if(!used_zeros && addr[7] == 0) {
os << "::";
}
os << "]:" << byteswapOnLittleEndian(m_addr.sin6_port);
return os;
}
UnixAddress
派生自Address,表示UNIX域套接字地址
// 继承自Address,表示UNIX域套接字地址
class UnixAddress : public Address {
public:
typedef std::shared_ptr
<UnixAddress> ptr;
UnixAddress(); // 构造函数(默认)
UnixAddress(const std::string& path); // 构造函数(通过路径字符串)
const sockaddr* getAddr() const override; // 获取sockaddr指针
sockaddr* getAddr() override; // 获取sockaddr指针(非const版本)
socklen_t getAddrLen() const override; // 获取地址长度(socklen_t类型)
void setAddrLen(uint32_t v); // 设置地址长度
std::ostream& insert(std::ostream& os) const override; // 将地址转成字符串插入到流
private:
struct sockaddr_un m_addr; // sockaddr_un结构体,存放UNIX地址
socklen_t m_length; // 地址长度
};
// UnixAddress
static const size_t MAX_PATH_LEN = sizeof(((sockaddr_un*)0)->sun_path) - 1;
UnixAddress::UnixAddress() {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sun_family = AF_UNIX;
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;
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");
}
memcpy(m_addr.sun_path, path.c_str(), m_length);
m_length += offsetof(sockaddr_un, sun_path);
}
sockaddr* UnixAddress::getAddr() {
return (sockaddr*)&m_addr;
}
const sockaddr* UnixAddress::getAddr() const {
return (sockaddr*)&m_addr;
}
socklen_t UnixAddress::getAddrLen() const {
return m_length;
}
void UnixAddress::setAddrLen(uint32_t v) {
m_length = v;
}
std::ostream& UnixAddress::insert(std::ostream& os) const {
if(m_length > offsetof(sockaddr_un, sun_path)
&& m_addr.sun_path[0] == '\0') {
return os << "\\0" << std::string(m_addr.sun_path + 1,
m_length - offsetof(sockaddr_un, sun_path) - 1);
}
return os << m_addr.sun_path;
}
UnknownAddress
表示未知地址类型
// 继承自Address,表示未知地址类型
class UnknownAddress : public Address {
public:
typedef std::shared_ptr
<UnknownAddress> ptr;
UnknownAddress(int famliy);
UnknownAddress(const sockaddr& addr);
sockaddr* getAddr() override;
const sockaddr* getAddr() const override;
socklen_t getAddrLen() const override;
std::ostream& insert(std::ostream& os) const override;
private:
sockaddr m_addr;
};
// UnknowAddress
UnknownAddress::UnknownAddress(int family) {
memset(&m_addr, 0, sizeof(m_addr));
m_addr.sa_family = family;
}
UnknownAddress::UnknownAddress(const sockaddr& addr) {
m_addr = addr;
}
sockaddr* UnknownAddress::getAddr() {
return (sockaddr*)&m_addr;
}
const sockaddr* UnknownAddress::getAddr() const {
return &m_addr;
}
socklen_t UnknownAddress::getAddrLen() const {
return sizeof(m_addr);
}
std::ostream& UnknownAddress::insert(std::ostream& os) const {
os << "[UnknownAddress family=" << m_addr.sa_family << "]";
return os;
}
关于字节序
网络字节序采用大端序,故需要为该地址模块提供自动适配的字节序转换功能
上面的各个地址类的实现中使用了sylar编写的字节序转换函数,可以根据主机的配置进行选择性编译,确保字节序正确
之前有一次修改了一点源码然后编译报错几百行,最后发现是endian.h命名和系统头文件重复了,最后明确制定了路径才解决问题
#ifndef __SYLAR_ENDIAN_H__
#define __SYLAR_ENDIAN_H__
#define SYLAR_LITTLE_ENDIAN 1
#define SYLAR_BIG_ENDIAN 2
#include <byteswap.h>
#include <stdint.h>
#include
<type_traits>
namespace sylar {
// SFINAE,根据模板函数参数类型编译不同版本
template<class T>
typename std::enable_if<sizeof(T) == sizeof(uint64_t), T>::type
byteswap(T value) {
return (T)bswap_64((uint64_t)value);
}
template<class T>
typename std::enable_if<sizeof(T) == sizeof(uint32_t), T>::type
byteswap(T value) {
return (T)bswap_32((uint32_t)value);
}
template<class T>
typename std::enable_if<sizeof(T) == sizeof(uint16_t), T>::type
byteswap(T value) {
return (T)bswap_16((uint16_t)value);
}
// 设置主机字节序宏
#if BYTE_ORDER == BIG_ENDIAN
#define SYLAR_BYTE_ORDER SYLAR_BIG_ENDIAN
#else
#define SYLAR_BYTE_ORDER SYLAR_LITTLE_ENDIAN
#endif
// 以下是对byteswapOnLittleEndian()和byteswapOnBigEndian()的条件编译
// byteswapOnLittleEndian()只在小端序机器上byteswap,大端序机器什么都不做(可以用来转换成网络字节序)
// byteswapOnBigEndian()只在大端序机器上byteswap,小端序机器什么都不做
#if SYLAR_BYTE_ORDER == SYLAR_BIG_ENDIAN // 大端序机器编译
template<class T>
T byteswapOnLittleEndian(T t) {
return t;
}
template<class T>
T byteswapOnBigEndian(T t) {
return byteswap(t);
}
#else // 小端序机器编译
template<class T>
T byteswapOnLittleEndian(T t) {
return byteswap(t);
}
template<class T>
T byteswapOnBigEndian(T t) {
return t;
}
#endif
}
#endif // __SYLAR_ENDIAN_H__
测试
#include "sylar/address.h"
#include "sylar/log.h"
sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
void test() {
std::vector<sylar::Address::ptr> addrs;
SYLAR_LOG_INFO(g_logger) << "begin";
bool v = sylar::Address::Lookup(addrs, "www.ayanami.blue");
SYLAR_LOG_INFO(g_logger) << "end";
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();
}
}
void test_iface() {
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) << "GetInterfaceAddress fail";
return;
}
for(auto& i : results) {
SYLAR_LOG_INFO(g_logger) << i.first << " - " << i.second.first->toString()
<< " - " << i.second.second;
}
}
void test_ipv4() {
auto addr = sylar::IPAddress::Create("www.ayanami.blue");
// auto addr = sylar::IPAddress::Create("127.0.0.8");
if(addr) {
SYLAR_LOG_INFO(g_logger) << addr->toString();
}
}
void test_addr() {
{
auto addr = sylar::IPv4Address::Create("192.168.123.114");
auto addr1 = addr->broadcastAddress(16);
auto addr2 = addr->networkAddress(16);
auto addr3 = addr->subnetAddress(16);
SYLAR_LOG_INFO(g_logger) << addr->toString();
SYLAR_LOG_INFO(g_logger) << addr1->toString();
SYLAR_LOG_INFO(g_logger) << addr2->toString();
SYLAR_LOG_INFO(g_logger) << addr3->toString();
}
SYLAR_LOG_INFO(g_logger) << "---------------------";
// {
// auto addr = sylar::IPv6Address::Create("fe80::5fdf:717c:30:d23f");
// auto addr1 = addr->broadcastAddress(128);
// auto addr2 = addr->networkAddress(128);
// auto addr3 = addr->subnetAddress(128);
// SYLAR_LOG_INFO(g_logger) << addr->toString();
// SYLAR_LOG_INFO(g_logger) << addr1->toString();
// SYLAR_LOG_INFO(g_logger) << addr2->toString();
// SYLAR_LOG_INFO(g_logger) << addr3->toString();
// }
}
int main(int argc, char** argv) {
test();
std::cout << "============================" << std::endl;
test_iface();
std::cout << "============================" << std::endl;
test_ipv4();
std::cout << "============================" << std::endl;
test_addr();
return 0;
}
可以看到测试结果符合预期

总结
后续网络编程可以很方便地创建和使用地址




φ( ̄∇ ̄o)!