[英]How to get host name from ip address or sockaddr_in structure (C++/WinAPI)?
[英]How to get IP address from sockaddr
我想在調用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;
從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
),我認為應該是線程安全的。
這個長數字是整數形式的IP地址(畢竟IP地址只是一個整數;當我們將八位字節分開並將其放入點表示法時,人們更容易使用它)。
您可以使用inet_ntoa
將整數值轉換為標准點表示法。
你得到的是IP地址的原始32位整數表示。 要獲得熟悉的點分隔字符串,請使用以下函數:
char * inet_ntoa(struct in_addr addr);
這會將整數轉換為靜態字符串。
以下內容摘自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);
}
雖然這些都是很好的答案,但他們用-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]
Thrid嘗試:inet_pton,反正更現代,線程安全,無效*
char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
好的,有效。 人類可讀的十進制和點字符串在peer_addr_str
。
您可以使用適用於Linux和Windows的getnameinfo函數。 從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答案的清理。 他的回答有一個接受電話的工作范例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.