如何从sockaddr获取IP地址

时间:2009-12-01 06:04:42

标签: c++ sockets ip-address

我想在调用accept后尝试获取客户端的IP地址。这是我到目前为止所拥有的,但我最终得到一些显然不是IP地址的长号码。可能有什么不对?

int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);

sockaddr_in client;
client.sin_family = AF_INET;
socklen_t c_len = sizeof(client);

int acc_tcp_sock = accept(tcp_sock, (sockaddr*)&client, &c_len);
cout << "Connected to: " << client.sin_addr.s_addr << endl;

6 个答案:

答案 0 :(得分:30)

http://beej.us/guide/bgnet/examples/client.c看到:

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET)
        return &(((struct sockaddr_in*)sa)->sin_addr);
    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

// [...]

struct addrinfo *p;
char s[INET6_ADDRSTRLEN];
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);

它使用inet_ntop,它优先于inet_ntoa(非线程安全),因为它处理IPv4和IPv6(AF_INETAF_INET6)并且应该是线程 - 我认为安全。

答案 1 :(得分:26)

那个长数 IP地址,整数形式(毕竟IP地址只是一个整数;当我们将八位字节分开并将其放入时,人们更容易使用它点符号)。

You can use inet_ntoa将整数值转换为标准点表示法。

答案 2 :(得分:6)

你得到的是IP地址的原始32位整数表示。 要获得熟悉的点分隔字符串,请使用以下函数:

 char * inet_ntoa(struct in_addr addr);

将整数转换为静态字符串。

答案 3 :(得分:3)

以下内容来自示例https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c

              struct sockaddr in_addr;
              socklen_t in_len;
              int infd;
              char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

              in_len = sizeof in_addr;
              infd = accept (sfd, &in_addr, &in_len);
              if (infd == -1)
                {
                  if ((errno == EAGAIN) ||
                      (errno == EWOULDBLOCK))
                    {
                      /* We have processed all incoming
                         connections. */
                      break;
                    }
                  else
                    {
                      perror ("accept");
                      break;
                    }
                }

                 s = getnameinfo (&in_addr, in_len,
                 hbuf, sizeof hbuf,
                 sbuf, sizeof sbuf,
                 NI_NUMERICHOST | NI_NUMERICSERV);
                 if (s == 0){
                     printf("Accepted connection on descriptor %d "
                         "(host=%s, port=%s)\n", infd, hbuf, sbuf);
                 }

答案 4 :(得分:0)

虽然这些都是很好的答案,但他们用-pedantic -std=c99 -Werror打破了我的编译。

来自 man 7 socket

  

要允许将任何类型的套接字地址传递给套接字API中的接口,请定义类型struct sockaddr。的目的   这种类型纯粹是为了允许转换特定于域的套接字地址   类型为&#34;泛型&#34;类型,以避免编译器有关类型的警告   调用套接字API时不匹配。

要获取此页面中的所有相关数据,请参阅glibc-2.17(RHEL7),我看到

/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];       /* Address data.  */
  };

其中SOCKADDR_COMMON是uint16_t。所以总大小是16B。

IP(互联网协议)特定于域名,来自 man 7 ip

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

首先尝试

inet_ntoa( ((struct sockaddr_in) peer_addr).sin_addr )

问题

error: conversion to non-scalar type requested

第二次尝试

 inet_ntoa( ((struct sockaddr_in *) &peer_addr)->sin_addr ) ));

问题

error: dereferencing type-punned pointer might break strict-aliasing rules [-Werror=strict-aliasing]

Thrid尝试:inet_pton,反正更现代,线程安全,无效*

char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
好的,有效。人类可读的十进制和点字符串位于peer_addr_str

答案 5 :(得分:0)

您可以使用适用于Linux和Windows的getnameinfo功能。来自Linux手册页https://linux.die.net/man/3/getnameinfo

  

getnameinfo - 以协议无关的方式进行地址到名称的翻译

C / C ++(Linux)示例:

#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>

char host[NI_MAXHOST]; // to store resulting string for ip
if (getnameinfo((sockaddr*)&client, c_len,
                  host, NI_MAXHOST, // to try to get domain name don't put NI_NUMERICHOST flag
                  NULL, 0,          // use char serv[NI_MAXSERV] if you need port number
                  NI_NUMERICHOST    // | NI_NUMERICSERV
               ) != 0) {
   //handle errors
} else {
    printf("Connected to: %s\n", host);     
}

这是对@enthusiasticgeek答案的清理。他的回答有一个接受电话的实例。