Je veux essayer d'obtenir l'adresse IP d'un client après avoir appelé accept. C'est ce que j'ai jusqu'à présent, mais je finis par obtenir un numéro long qui n'est clairement pas une adresse IP. Qu'est-ce qui pourrait mal?
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;
Ce long numéro est l'adresse IP, sous forme d'entier (une adresse IP n'est qu'un entier, après tout; il est juste plus facile à utiliser lorsque les gens séparer les octets et le mettre en notation par points).
Vous pouvez utiliser inet_ntoa
pour convertir la valeur entière en notation par points standard.
Vu de 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);
Il utilise inet_ntop
, qui est préférable à inet_ntoa
(non thread-safe) car il gère IPv4 et IPv6 (AF_INET
et AF_INET6
) et devrait être thread-safe je pense.
ce que vous obtenez est la représentation entière brute de 32 bits de l'adresse IP. pour obtenir la chaîne familière séparée par des points, utilisez la fonction:
char * inet_ntoa(struct in_addr addr);
qui convertira l'entier en une chaîne statique.
Ce qui suit est tiré de l'exemple 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);
}
Vous pouvez utiliser la fonction getnameinfo disponible pour Linux et Windows. Depuis les pages de manuel Linux https://linux.die.net/man/3/getnameinfo :
getnameinfo - traduction d'adresse à nom de manière indépendante du protocole
Exemple pour 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);
}
Ceci est un nettoyage de la réponse @enthusiasticgeek. Sa réponse a un exemple de travail avec accepter l'appel.
Bien que ce soient de bonnes réponses, ils ont cassé ma compilation avec -pedantic -std=c99 -Werror
.
De prise homme 7
Pour permettre à tout type d'adresse de socket d'être transmis aux interfaces dans l'API de sockets, le type
struct sockaddr
est défini. Le but de ce type est purement de permettre le transtypage de types d'adresse de socket spécifiques au domaine en un type "générique", afin d'éviter les avertissements du compilateur concernant les incompatibilités de type dans les appels à l'API sockets.
Pour obtenir toutes les données pertinentes dans cette page, à partir de glibc-2.17 (RHEL7) je vois
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
où SOCKADDR_COMMON est un uint16_t
. La taille totale est donc de 16B.
IP (protocole Internet) Domaine spécifique, de 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 */
};
Premier essai
inet_ntoa( ((struct sockaddr_in) peer_addr).sin_addr )
Problème
error: conversion to non-scalar type requested
Deuxième essai
inet_ntoa( ((struct sockaddr_in *) &peer_addr)->sin_addr ) ));
Problème
error: dereferencing type-punned pointer might break strict-aliasing rules [-Werror=strict-aliasing]
Troisième essai: inet_pton, plus moderne de toute façon, thread-safe, prend vide *
char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
ok, ça marche. La chaîne décimale et points lisible par l'homme est en peer_addr_str
.