2009-12-01 134 views
22

我想在調用accept之後嘗試獲取客戶機的ip地址。這是我迄今爲止,但我最終得到一個很長的數字,顯然不是一個IP地址。什麼可能是錯的?如何從sockaddr獲取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; 

回答

24

那一長串數字的IP地址,以整數形式(一個IP地址僅僅是一個整數,畢竟,它只是更容易,當我們分開拆分八位位組人使用,放入點符號)。

You can use inet_ntoa將整數值轉換爲標準點符號。

+0

這正是我需要做的,謝謝。 – adhanlon

+1

請注意,現在'inet_ntoa'被視爲棄用。你應該改用支持更多格式的'inet_ntop'。 –

+0

請記住'inet_ntoa'不是線程安全的 –

6

你得到的是IP地址的原始32位整數表示。 獲得熟悉的圓點分隔的字符串,使用功能:

char * inet_ntoa(struct in_addr addr); 

,將整數轉換爲靜態字符串。

28

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_INETAF_INET6),應是線程安全的,我認爲。

3

下面是來自例子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); 
       } 
0

採取雖然這些都是很好的答案,他們打破了我的編譯-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(互聯網協議)特定領域的,從人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

0

您可以使用可用於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答案的答案。他的回答有一個接受電話的例子。