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;
その長い数字は、整数形式のIPアドレスです(結局、IPアドレスは単なる整数です。オクテットを分けて、ドット表記にします)。
inet_ntoa
を使用して、整数値を標準のドット表記に変換できます。
から見た 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
を使用します。これは、IPv4およびIPv6(inet_ntoa
およびAF_INET
)を処理し、スレッドセーフである必要があるため、AF_INET6
(スレッドセーフではない)よりも優先されます。 。
あなたが得ているのは、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);
}
LinuxおよびWindowsで使用可能なgetnameinfo関数を使用できます。 Linux manページから 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の回答を整理したものです。彼の答えには、受諾呼び出しの実例があります。
これらは良い答えですが、-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(インターネットプロトコル)ドメイン固有、from 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]
トライトライ:inet_pton、とにかくもっと現代的、スレッドセーフ、void *
char peer_addr_str[ INET_ADDRSTRLEN ];
inet_ntop( AF_INET, &peer_addr, peer_addr_str, INET_ADDRSTRLEN );
わかりました。人間が読める10進数とドットの文字列はpeer_addr_str
にあります。