Как получить IP-адрес от sockaddr
Я хочу попытаться получить 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;
Ответы
Ответ 1
Этот длинный номер - это IP-адрес в целочисленной форме (в конце концов, IP-адрес - это просто целое число, а просто для людей просто использовать, когда мы разделяем октеты и помещаем их в точечную нотацию).
Вы можете использовать inet_ntoa
для преобразования целочисленного значения в стандартную точечную нотацию.
Ответ 2
Видно из 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
) и должен быть потокобезопасным, я думаю.
Ответ 3
то, что вы получаете, - это 32-битное целочисленное представление IP-адреса.
для получения знакомой строки, разделенной точкой, используйте функцию:
char * inet_ntoa(struct in_addr addr);
который преобразует целое число в статическую строку.
Ответ 4
Ниже приведен пример 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);
}
Ответ 5
Пока это хорошие ответы, они сломали мой компилятор с помощью -pedantic -std=c99 -Werror
.
От разъема man 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 (интернет-протокол) Домен, от человека 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 try: inet_pton, более современный в любом случае, потокобезопасный, принимает пустоту *
char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
ok, работает. Считываемая строка десятичной и точной строки находится в peer_addr_str
.
Ответ 6
Вы можете использовать функцию getnameinfo, доступную для Linux и Windows. Из справочных страниц Linux https://linux.die.net/man/3/getnameinfo:
getnameinfo - преобразование адреса для имени не зависит от протокола
Пример для 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. У его ответа есть рабочий пример с принятием вызова.