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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java 枚举活用

    Java 枚举活用

    2021年8月26日
    55
  • 端口分类_宽带端口是什么样的

    端口分类_宽带端口是什么样的一、端口通俗地讲,端口(Port)就是电脑向网络开放的信息出入“门户”。和小区大门不同的是,在电脑上这种“门户”有个256×256(65535)个,而且它们还有多种状态。1.端口的分类根据端口和服务的绑定情况,端口可分为公认端口、注册端口和动态端口。公认端口:0~1023。这个范围内的端口系统一般保留给一些常用的系统服务,比如WEB服务使用80端口,FTP服务使用21端口

    2025年9月16日
    5
  • FarPoint Spread 基础知识

    FarPoint Spread 基础知识1.获得当前行的行号,列号,总列数,总行数introwCount=fpSpread1.ActiveSheet.RowCount;intcolCount=fpSpread1.ActiveSheet.Columns.Count;intactiveRow=fpSpread1.Activ…

    2025年6月17日
    3
  • SQL语法(五) 多表联合查询

    SQL语法(五) 多表联合查询前言当需要获取的数据分布在多张中,考虑使用联合查询,本章将学习两种查询方式(sql92/sql99)范例1.笛卡儿积将多个表的数据进行一一对应,所得到结果为多表的笛卡尔积。结果的数量为所有表的数量的乘积。–SQL92方式–表名以逗号隔开实现多表查询–SQL99方式–使用crossjoin关键字2.等值连接筛选&不等…

    2022年6月12日
    24
  • Android listView选择颜色状态

    Android listView选择颜色状态(1)listviewitem选择监听    listview.setOnItemClickListener(newAdapterView.OnItemClickListener(){      @Override      publicvoidonItemClick(AdapterViewparent,Viewview,intpos

    2022年7月16日
    15
  • 程序员 你读过的书,藏着自己的命运 | 技术类(一)

    做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!程序员 你读过的书,藏着自己的命运 | 技术类(一)本文的标题来自CSDN的征文题目,让我感触很深。读过书的让你成长,读过的书改变着我们的思想,读过的书也不断成就我们。我也写了一篇征文:程序世界,平凡的我。本文整理的大多数书籍,都是针对Java程序员的,有一些基础类的书籍,如计算机基础 ,适合每个程序员阅读。整…

    2022年3月1日
    46

发表回复

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

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