C++ 如何从 sockaddr 获取 IP 地址
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1824279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get IP address from sockaddr
提问by adhanlon
I want to try and get the ip address of a client after calling accept. This is what I have so far, but I just end up getting some long number that is clearly not an ip address. What could be wrong?
我想在调用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;
回答by ofavre
Seen from http://beej.us/guide/bgnet/examples/client.c:
从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);
It uses inet_ntop
, which is preferred over inet_ntoa
(non thread-safe) as it handles IPv4 and IPv6 (AF_INET
and AF_INET6
) and should be thread-safe I think.
它使用inet_ntop
, 优于inet_ntoa
(非线程安全),因为它处理 IPv4 和 IPv6(AF_INET
和AF_INET6
),我认为应该是线程安全的。
回答by James McNellis
That long number isthe IP address, in integer form (an IP address is just an integer, after all; it's just easier for people to use when we split the octets apart and put it into dot notation).
那个长数字是整数形式的 IP 地址(毕竟,IP 地址只是一个整数;当我们将八位字节分开并将其放入点表示法时,人们会更容易使用)。
You can use inet_ntoa
to convert the integer value to standard dot notation.
您可以使用inet_ntoa
将整数值转换为标准点表示法。
回答by Alon
what your are getting is the raw 32 bit integer representation of the IP address. to get the familiar dot separated string, use the function:
您得到的是 IP 地址的原始 32 位整数表示。要获得熟悉的点分隔字符串,请使用以下函数:
char * inet_ntoa(struct in_addr addr);
that will convert the integer to a static string.
这会将整数转换为静态字符串。
回答by enthusiasticgeek
The following is taken from the example https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c
以下摘自示例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);
}
回答by FDS
While these are good answers, they broke my compile with -pedantic -std=c99 -Werror
.
虽然这些是很好的答案,但它们破坏了我的编译-pedantic -std=c99 -Werror
。
From man 7 socket
从man 7 插座
To allow any type of socket address to be passed to interfaces in the sockets API, the type
struct sockaddr
is defined. The purpose of this type is purely to allow casting of domain-specific socket address types to a "generic" type, so as to avoid compiler warnings about type mismatches in calls to the sockets API.
为了允许将任何类型的套接字地址传递给套接字 API 中的接口,
struct sockaddr
定义了类型。这种类型的目的纯粹是为了允许将特定于域的套接字地址类型转换为“通用”类型,以避免编译器在调用套接字 API 时发出有关类型不匹配的警告。
To get all the relevant data in this page, from glibc-2.17 (RHEL7) I see
要获取此页面中的所有相关数据,从 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. */
};
where SOCKADDR_COMMON is a uint16_t
. So total size is 16B.
其中 SOCKADDR_COMMON 是一个uint16_t
. 所以总大小是16B。
IP (internet protocol) Domain specific, from man 7 ip:
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 */
};
First try
第一次尝试
inet_ntoa( ((struct sockaddr_in) peer_addr).sin_addr )
Problem
问题
error: conversion to non-scalar type requested
Second try
第二次尝试
inet_ntoa( ((struct sockaddr_in *) &peer_addr)->sin_addr ) ));
Problem
问题
error: dereferencing type-punned pointer might break strict-aliasing rules [-Werror=strict-aliasing]
Thrid try: inet_pton, more modern anyways, thread safe, takes void*
第三次尝试:inet_pton,无论如何更现代,线程安全,需要 void*
char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
ok, works. Human readable decimal-and-dots string is in peer_addr_str
.
好的,有效。人类可读的十进制和点字符串在peer_addr_str
.
回答by littlewhywhat
You can use getnameinfo function that is available for Linux and Windows. From Linux man pages https://linux.die.net/man/3/getnameinfo:
您可以使用适用于 Linux 和 Windows 的 getnameinfo 函数。从 Linux 手册页https://linux.die.net/man/3/getnameinfo:
getnameinfo - address-to-name translation in protocol-independent manner
getnameinfo - 以独立于协议的方式进行地址到名称的转换
Example for C/C++ (Linux):
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);
}
This is clean up of @enthusiasticgeek answer. His answer has a working example with accept call.
这是对@functionalicgeek 答案的清理。他的回答有一个接受呼叫的工作示例。