【问题标题】:Portable compact representation of IP addressIP地址的便携式紧凑表示
【发布时间】:2012-03-06 15:36:19
【问题描述】:

我有一个在 Linux 上使用 Berkley 套接字 API 的 C++ 程序。我有一个连接的一端向客户端发送两个 IP 地址。我可以使用inet_ntop()inet_pton() 来表示这些,但这会使消息长度为2*INET6_ADDRSTRLEN,即92 字节。对于两个 IP 地址来说,这似乎有点多。是否有可移植的、紧凑的 IP 地址二进制表示(它必须同时适用于 IPv4 和 IPv6)。

【问题讨论】:

  • IP 地址(在 IP 标头中使用)默认是二进制的。对于 v4,这归结为 8 字节,对于 2 个地址,对于 v6 则为 32 字节。
  • 为什么不以原始二进制形式(适用时按网络字节顺序)发送地址,其中一个字节首先包含地址系列 (AF_INET/AF_INET6)?
  • 我知道。我需要一个可移植且独立于体系结构的二进制表示,我可以通过网络发送。
  • 它是独立于架构的,格式的字节序非常明确。只是不要天真地将其解释为 int 或类似的东西。
  • @Linux_iOS.rb.cpp.c.lisp.m.sh - 如果你有一个addrinfo,那就是.ai_addr.ai_addrlen。见this serverclient

标签: c++ sockets ip-address


【解决方案1】:

如果您有 addrinfo,请发送 .ai_addr.ai_addrlen

试试这两个程序:

send_sockaddr.cc:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <cstdio>
#include <cerrno>
#include <cstdlib>

int main (int ac, char **av) {

  if(ac != 3) {
    fprintf(stderr, "Usage: %s hostname portnumber\n", *av);
    return 1;
  }

  struct addrinfo *res0;
  struct addrinfo hints = { AI_CANONNAME, 0, SOCK_DGRAM };
  int rc = getaddrinfo(av[1], av[2], &hints, &res0);
  if(rc) {
    fprintf(stderr, "%s/%s: %s\n", av[1], av[2], gai_strerror(rc));
    return 1;
  }

  char *name = res0->ai_canonname;
  for(struct addrinfo *res = res0; res; res=res->ai_next) {
    fprintf(stderr, "%s: %04X/%04X/%04X ", name, res->ai_family, res->ai_socktype, res->ai_protocol);
    int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(fd < 0) {
      perror("socket");
      continue;
    }

    rc = connect(fd, res->ai_addr, res->ai_addrlen);
    if(rc < 0) {
      perror("connect");
      continue;
    }
    fprintf(stderr, "Connected (%d)\n", fd);
    *(unsigned short*)res->ai_addr = htons(*(unsigned short*)res->ai_addr);
    rc = send(fd, res->ai_addr, res->ai_addrlen, 0);
    *(unsigned short*)res->ai_addr = ntohs(*(unsigned short*)res->ai_addr);
    if(rc < 0) {
      perror("send");
    }
    close(fd);
  }
  freeaddrinfo(res0);
}

listen_sockaddr.cc:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <cstdio>
#include <cerrno>
#include <cstdlib>
#include <poll.h>
#include <vector>
#include <arpa/inet.h>

int main (int ac, char **av) {

  if(ac != 2) {
    fprintf(stderr, "Usage: %s portnumber\n", *av);
    return 1;
  }

  struct addrinfo *res0;
  struct addrinfo hints = { 0, 0, SOCK_DGRAM };
  int rc = getaddrinfo(0, av[1], &hints, &res0);
  if(rc) {
    fprintf(stderr, "%s/%s: %s\n", av[1], av[2], gai_strerror(rc));
    return 1;
  }

  char *name = res0->ai_canonname;
  std::vector<pollfd> fds;
  for(struct addrinfo *res = res0; res; res=res->ai_next) {
    fprintf(stderr, "%s: ", name);
    int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(fd < 0) {
      perror("socket");
      continue;
    }

    rc = bind(fd, res->ai_addr, res->ai_addrlen);
    if(rc < 0) {
      perror("bind");
      continue;
    }
    fprintf(stderr, "Bound (%d)\n", fd);
    fds.push_back(pollfd({fd, POLLIN}));
  }
  freeaddrinfo(res0);

  while( (rc = poll( &fds[0], fds.size(), -1)) > 0 ) {
    for(size_t i = 0; i < fds.size(); ++i) {
      pollfd& pfd = fds[i];
      if(!pfd.revents)
        continue;
      pfd.revents = 0;

      union {
        sockaddr s;
        sockaddr_in sin;
        sockaddr_in6 sin6;
      } u;
      rc = recv(pfd.fd, &u, sizeof u, 0);
      if(rc < 0) {
        perror("recv");
        continue;
      }
      fprintf(stderr, "Received %d bytes\n", rc);

      char str[256];
      switch(ntohs(u.s.sa_family)) {
      case AF_INET:
        if(inet_ntop(AF_INET, &u.sin.sin_addr, str, sizeof str)) {
          fprintf(stderr, "AF_INET %s\n", str);
        } else {
          fprintf(stderr, "AF_INET unknown\n");
        }
        break;
      case AF_INET6:
        if(inet_ntop(AF_INET6, &u.sin6.sin6_addr, str, sizeof str)) {
          fprintf(stderr, "AF_INET6 %s\n", str);
        } else {
          fprintf(stderr, "AF_INET6 unknown\n");
        }
        break;
      default:
        fprintf(stderr, "UNKNOWN\n");
        break;
      }
    }
  }
}

【讨论】:

    【解决方案2】:

    实际上,IP 地址本身并不是数字,因此字节表示始终遵循 Big-Endian。至少我不知道有什么不同的平台。它只是不作为数字处理,而是作为 4 个字节处理。

    【讨论】:

    • 特定的 Berkeley 套接字addrinfo struct 怎么样?
    • 不,addrinfo 既不便携也不能够通过网络传输。但in_addrin6_addr 可能是。
    • @cooky451:嗯,我会反对这个。您不是在这里混淆了表示和数据吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 2015-09-26
    • 2014-03-15
    • 2011-02-23
    • 2015-03-26
    • 1970-01-01
    相关资源
    最近更新 更多