Как получить IP-адрес от sockaddr - PullRequest
24 голосов
/ 01 декабря 2009

Я хочу попытаться получить IP-адрес клиента после вызова accept. Это то, что у меня есть, но я просто получаю длинный номер, который явно не является 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 ]

30 голосов
/ 09 февраля 2012

При просмотре с 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_INET и AF_INET6) и должен быть поточно-ориентированным, я думаю.

27 голосов
/ 01 декабря 2009

Это длинное число - это IP-адрес в целочисленной форме (в конце концов, IP-адрес - это просто целое число; людям проще использовать, когда мы разделяем октеты на части и помещаем их в точку обозначения).

Вы можете использовать inet_ntoa для преобразования целочисленного значения в стандартное обозначение точки.

6 голосов
/ 01 декабря 2009

то, что вы получаете - это 32-битное целое представление IP-адреса. чтобы получить знакомую строку, разделенную точкой, используйте функцию:

 char * inet_ntoa(struct in_addr addr);

, который преобразует целое число в статическую строку.

3 голосов
/ 28 мая 2013

Следующее взято из примера 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);
                 }
0 голосов
/ 20 июня 2017

Вы можете использовать функцию getnameinfo, которая доступна для Linux и Windows. Из справочных страниц 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. В его ответе есть рабочий пример с оператором accept call.

0 голосов
/ 12 мая 2015

Хотя это хорошие ответы, они сломали мою компиляцию с -pedantic -std=c99 -Werror.

Из человек 7 розетка

Чтобы разрешить передачу любого типа адреса сокета интерфейсам в API сокетов, определен тип struct sockaddr. Цель этот тип предназначен исключительно для разрешения приведения адреса сокета, специфичного для домена типы к «универсальному» типу, чтобы избежать предупреждений компилятора о типе несоответствия в вызовах 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]

Третья попытка: inet_pton, в любом случае более современная, поточно-безопасная, аннулирует *

char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );

ок, работает. Читаемая человеком строка с десятичными точками находится в peer_addr_str.

...