InetAddress

2022-09-10 16:43:53 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

InetAddress类就是封装了IPv4地址和IPv6地址,比较简单。这是muduo库中少有的值语义的类,所以继承的是copyable。实际上copyable只是强调可以拷贝,并没有实际意义。即使不继承该类还是可以copy。

代码语言:javascript复制
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库目前好像也没有使用。

我更感兴趣的是头文件中的两个地方:

代码语言:javascript复制
  union
  {
    struct sockaddr_in addr_;
    struct sockaddr_in6 addr6_;
  };

const struct sockaddr* sockaddr_cast(const struct sockaddr_in6* addr);

实际上sockaddr_cast是在SocketsOps.cc中实现的。

代码语言:javascript复制
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两种类型,这两种类型是这样的:

代码语言:javascript复制
//     /* 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做了测试,

代码语言:javascript复制
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类中,有两个成员函数:

代码语言:javascript复制
  const struct sockaddr* getSockAddr() const { return sockets::sockaddr_cast(&addr6_); }
  void setSockAddrInet6(const struct sockaddr_in6& addr6) { addr6_ = addr6; }

这里就只使用了sockaddr_in6类型。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152217.html原文链接:https://javaforall.cn

0 人点赞