大家好,又见面了,我是你们的朋友全栈君。
InetAddress类就是封装了IPv4地址和IPv6地址,比较简单。这是muduo库中少有的值语义的类,所以继承的是copyable。实际上copyable只是强调可以拷贝,并没有实际意义。即使不继承该类还是可以copy。
InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6)
{
static_assert(offsetof(InetAddress, addr6_) == 0, "addr6_ offset 0");
static_assert(offsetof(InetAddress, addr_) == 0, "addr_ offset 0");
if (ipv6)
{
memZero(&addr6_, sizeof addr6_);
addr6_.sin6_family = AF_INET6;
in6_addr ip = loopbackOnly ? in6addr_loopback : in6addr_any;
addr6_.sin6_addr = ip;
addr6_.sin6_port = sockets::hostToNetwork16(port);
}
else
{
memZero(&addr_, sizeof addr_);
addr_.sin_family = AF_INET;
in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);
addr_.sin_port = sockets::hostToNetwork16(port);
}
}
InetAddress::InetAddress(StringArg ip, uint16_t port, bool ipv6)
{
if (ipv6)
{
memZero(&addr6_, sizeof addr6_);
sockets::fromIpPort(ip.c_str(), port, &addr6_);
}
else
{
memZero(&addr_, sizeof addr_);
sockets::fromIpPort(ip.c_str(), port, &addr_);
}
}
string InetAddress::toIpPort() const
{
char buf[64] = "";
sockets::toIpPort(buf, sizeof buf, getSockAddr());
return buf;
}
string InetAddress::toIp() const
{
char buf[64] = "";
sockets::toIp(buf, sizeof buf, getSockAddr());
return buf;
}
uint32_t InetAddress::ipNetEndian() const
{
assert(family() == AF_INET);
return addr_.sin_addr.s_addr;
}
uint16_t InetAddress::toPort() const
{
return sockets::networkToHost16(portNetEndian());
}
static __thread char t_resolveBuffer[64 * 1024];
bool InetAddress::resolve(StringArg hostname, InetAddress* out)
{
assert(out != NULL);
struct hostent hent;
struct hostent* he = NULL;
int herrno = 0;
memZero(&hent, sizeof(hent));
int ret = gethostbyname_r(hostname.c_str(), &hent, t_resolveBuffer, sizeof t_resolveBuffer, &he, &herrno);
if (ret == 0 && he != NULL)
{
assert(he->h_addrtype == AF_INET && he->h_length == sizeof(uint32_t));
out->addr_.sin_addr = *reinterpret_cast<struct in_addr*>(he->h_addr);
return true;
}
else
{
if (ret)
{
LOG_SYSERR << "InetAddress::resolve";
}
return false;
}
}
void InetAddress::setScopeId(uint32_t scope_id)
{
if (family() == AF_INET6)
{
addr6_.sin6_scope_id = scope_id;
}
}
首先想说一下这个宏:size_t offsetof(type, member);该宏返回的是一个结构成员相对于结构开头的字节偏移量。type为结构体,member为结构体中的某个成员。
该类的成员函数基本上就是实现的端口号和IP与套接字地址结构的转换功能。除此之外还有两个成员函数是resolve和setScopeId,reSolve是DNS解析时使用的,在muduo库中好像并没有使用,setScopeId是针对IPv6的,muduo库目前好像也没有使用。
我更感兴趣的是头文件中的两个地方:
union
{
struct sockaddr_in addr_;
struct sockaddr_in6 addr6_;
};
const struct sockaddr* sockaddr_cast(const struct sockaddr_in6* addr);
实际上sockaddr_cast是在SocketsOps.cc中实现的。
const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in6* addr)
{
return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
}
struct sockaddr* sockets::sockaddr_cast(struct sockaddr_in6* addr)
{
return static_cast<struct sockaddr*>(implicit_cast<void*>(addr));
}
const struct sockaddr* sockets::sockaddr_cast(const struct sockaddr_in* addr)
{
return static_cast<const struct sockaddr*>(implicit_cast<const void*>(addr));
}
这个接口将sockaddr_in和sockaddr_in6类型的指针转为sockaddr类型的指针。因为像accept之类的函数传参都是使用sockaddr类型的指针。在SocketsOps.cc中实现了sockadd_in6和sockaddr_in两个版本的转换,但实际上好像只使用了sockaddr_in6版本的,这里其实就是因为作者使用了union放sockaddr_in和sockaddr_in6两种类型,这两种类型是这样的:
// /* Structure describing an Internet socket address. */
// struct sockaddr_in {
// sa_family_t sin_family; /* address family: AF_INET */
// uint16_t sin_port; /* port in network byte order */
// struct in_addr sin_addr; /* internet address */
// };
// /* Internet address. */
// typedef uint32_t in_addr_t;
// struct in_addr {
// in_addr_t s_addr; /* address in network byte order */
// };
// struct sockaddr_in6 {
// sa_family_t sin6_family; /* address family: AF_INET6 */
// uint16_t sin6_port; /* port in network byte order */
// uint32_t sin6_flowinfo; /* IPv6 flow information */
// struct in6_addr sin6_addr; /* IPv6 address */
// uint32_t sin6_scope_id; /* IPv6 scope-id */
// };
实际上相当于sockaddr_in6包含了sockaddr_in,前两个成员是一模一样的, 第三个成员也是一个类型的,而且作者还专门使用static_assert做了测试,
static_assert(sizeof(InetAddress) == sizeof(struct sockaddr_in6),
"InetAddress is same size as sockaddr_in6");
static_assert(offsetof(sockaddr_in, sin_family) == 0, "sin_family offset 0");
static_assert(offsetof(sockaddr_in6, sin6_family) == 0, "sin6_family offset 0");
static_assert(offsetof(sockaddr_in, sin_port) == 2, "sin_port offset 2");
static_assert(offsetof(sockaddr_in6, sin6_port) == 2, "sin6_port offset 2");
可以把sockaddr_in认为是sockaddr_in6的一部分,所以即使InetAddress表示的是IPv4的类型,使用sockaddr_in6的指针也是没有关系的。不过就是在IPv4的时候只使用该指针指向的内容的前半部分就可以了,那部分就是sockaddr_in。
这样就不管是IPv4还是IPv6,只使用一个版本就可以。例如在InetAddress类中,有两个成员函数:
const struct sockaddr* getSockAddr() const { return sockets::sockaddr_cast(&addr6_); }
void setSockAddrInet6(const struct sockaddr_in6& addr6) { addr6_ = addr6; }
这里就只使用了sockaddr_in6类型。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152217.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...