2009-12-01 22 views
22

Quiero intentar obtener la dirección IP de un cliente después de llamar a accept. Esto es lo que tengo hasta ahora, pero acabo obteniendo un número largo que claramente no es una dirección IP. ¿Qué podría estar mal?Cómo obtener la dirección IP de sockaddr

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; 

Respuesta

24

Esa larga serie es la dirección IP, en forma de número entero (una dirección IP es un número entero, después de todo, es sólo más fácil para la gente a usar cuando nos separamos los octetos separados y la ponen en punto notación).

You can use inet_ntoa para convertir el valor entero en notación de puntos estándar.

+0

eso es exactamente lo que tenía que hacer, gracias. – adhanlon

+1

Tenga en cuenta que ahora 'inet_ntoa' se considera obsoleta. En su lugar, debe usar 'inet_ntop', que admite más formatos. –

+0

Recuerde que 'inet_ntoa' no es seguro para subprocesos –

6

lo que está obteniendo es la representación entera en bruto de 32 bits de la dirección IP. para obtener el punto familiarizado cadena separada, utilice la función:

char * inet_ntoa(struct in_addr addr); 

que convertirá el número entero a una cadena estática.

28

visto desde 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); 

Utiliza inet_ntop, que se prefiere sobre inet_ntoa (no thread-safe) ya que maneja IPv4 e IPv6 (AF_INET y AF_INET6) y debe ser seguro para subprocesos que pienso.

3

La siguiente es tomado del ejemplo 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

Si bien estas son buenas respuestas, me rompieron la compilación con -pedantic -std=c99 -Werror.

De hombre 7 socket

para permitir que cualquier tipo de dirección de socket que se pasa a las interfaces de la API de sockets, se define el tipo struct sockaddr. El propósito de este tipo es simplemente permitir la conversión de tipos de direcciones de sockets específicos del dominio a un tipo "genérico", para evitar advertencias del compilador sobre el tipo no coinciden en las llamadas a la API de sockets.

para obtener todos los datos relevantes en esta página, desde glibc-2.17 (RHEL7) Veo

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

donde SOCKADDR_COMMON es un uint16_t. Entonces el tamaño total es 16B.

IP (protocolo de Internet) de dominio específico, desde el hombre 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 */ 
}; 

Pruebe primero

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

Problema

error: conversion to non-scalar type requested 

segundo intento

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

Problema

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

Thrid intento: inet_pton, más moderno de todos modos, hilo de seguridad, toma void *

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

bien, funciona. cadena decimal y puntos legible por el hombre está en peer_addr_str.

0

Puede utilizar getnameinfo función que está disponible para Linux y Windows. Desde las páginas de manual de Linux https://linux.die.net/man/3/getnameinfo:

getnameinfo - traducción de dirección-a-nombre en independiente del protocolo de manera

Ejemplo para 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);  
} 

Ésta es limpia de @enthusiasticgeek respuesta. Su respuesta tiene un ejemplo de trabajo con aceptar llamada.

Cuestiones relacionadas