InetAddress

InetAddressInetAddress类就是封装了IPv4地址和IPv6地址。比较简单,这是muduo库中少有的值语义的类,所以继承的是copyable。实际上copyable只是强调可以拷贝,并没有实际意义。即使不继承该类还是可以copy。InetAddress::InetAddress(uint16_tport,boolloopbackOnly,boolipv6){static_assert(offsetof(InetAddress,addr6_)==0,”addr6_offset0

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

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类型。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • OpenSSL安装成功记

    系统是XP,装了VC6.0和VS2005在Windows下安装OPENSSL,在网上看了很多帖子,照着别人的做,我的总是出错。出错信息cl.exe出错。我想也许是系统的问题吧。我想,把VC6卸载了试试吧。这一试终于看到了胜利的曙光。前提:安装过Perl。安装过程如下:把从官网上下载的openssl-0.9.8k.tar.tar解压缩到C盘。并把文件夹名改为openssl。它的安装目录便是C:/openssl。打开命令行。

    2022年4月9日
    58
  • 激活码pycharm_通用破解码

    激活码pycharm_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月16日
    37
  • XXE实体注入(超详细!)

    XXE实体注入(超详细!)可以把它理解为txt,就是存储文件的,读取并调用出来,这是最核心的将你的代码当成XXE代码,然后XXE再交给PHP去执行将1.txt的东西,放入test这个变量实体就是变量&test就是输出这个变量<scan></scan>只是一个声明格式,随便写什么,就算写成<abc></abc>都可以,只要满足<x></x>格式就行最主要的是访问的地址,file,http等协议都可以。XXE:XML外部实体注入,原理:.

    2022年5月23日
    65
  • 小代码改进

    小代码改进

    2021年8月29日
    62
  • Pytest(11)allure报告[通俗易懂]

    Pytest(11)allure报告[通俗易懂]前言allure是一个report框架,支持java的Junit/testng等框架,当然也可以支持python的pytest框架,也可以集成到Jenkins上展示高大上的报告界面。mac环境:

    2022年7月31日
    7
  • out of sync with file system

    out of sync with file system

    2021年7月15日
    75

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号