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


相关推荐

  • java8中的map与flatmap

    java8中的map与flatmap问题假如我们有这样一个需求给定单词列表[“Hello”,”World”],你想要返回列表[“H”,”e”,”l”,”o”,”W”,”r”,”d”],如果我们使用java实现这个需求,你会怎么实现呢?可能我们第一个想法是下面这种写法:words.stream().map(word->word.split(“”)).distinct().collect(toList());但是如果我们运行会发现这样的写法是不正确的,这个方法的问题在于,传递给map方法的Lambda为每个单词返回

    2022年6月4日
    54
  • asp.net中通过input file上传文件

    asp.net中通过input file上传文件前台: 后台:

    2022年7月17日
    12
  • 商汤组了「最强大脑」局,正儿八经解释为啥搞起电竞AI「建议收藏」

    商汤组了「最强大脑」局,正儿八经解释为啥搞起电竞AI「建议收藏」金磊发自凹非寺量子位报道|公众号QbitAI前不久,《星际争霸2》虫王iA周航加入商汤科技,担任AI研究员。堪称电竞职业玩家「转型最成功」的案例之一。而商汤作为一家以计算机视…

    2022年5月16日
    42
  • 轻量级网络什么意思_轻量级网络有哪些

    轻量级网络什么意思_轻量级网络有哪些轻量级网络ShuffleNetv1有事没事扯扯淡关注0.0732019.01.1014:50:55字数1,803阅读2,698ShuffleNetpaperShuffleNet是旷视科技提出的一种计算高效的CNN模型,其和MobileNet和SqueezeNet等一样主要是想应用在移动端。所以,ShuffleNet的设计目标也是如何利用有限的计算资源来达到最好的模型精度,这需要很好地在…

    2022年9月9日
    0
  • Android中跳转Activity有几种方法_java怎么跳转页面

    Android中跳转Activity有几种方法_java怎么跳转页面FLAG_ACTIVITY_NEW_TASK标记了FLAG_ACTIVITY_NEW_TASK,就一定会创建一个新的TASK吗,实验证明这是一个必要条件,还要结合taskAffinity来看,A.如果和H5Activity具有相应的亲戚存在了,那么只是会把H5Activity加入的亲缘的TASK中B.如果和H5Activity没有对应的亲缘关系的TASK,那么就会创建一个新的TASK了p

    2022年9月1日
    0
  • rgbd-slam_slam算法详解

    rgbd-slam_slam算法详解代码仓库:https://gitee.com/davidhan008/rgbd-slam

    2022年9月17日
    0

发表回复

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

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