便携式紧凑的IP地址表示

时间:2012-03-06 15:36:19

标签: c++ sockets ip-address

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

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

如果你有一个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;
      }
    }
  }
}