从 sockaddr 结构中获取 IPV4 地址

Posted

技术标签:

【中文标题】从 sockaddr 结构中获取 IPV4 地址【英文标题】:Getting IPV4 address from a sockaddr structure 【发布时间】:2010-11-19 13:31:47 【问题描述】:

如何将 IP 地址提取到字符串中?我找不到告诉我char sa_data[14] 是如何编码的参考资料。

【问题讨论】:

【参考方案1】:

只需将整个sockaddr 结构转换为sockaddr_in。然后你可以使用:

char *ip = inet_ntoa(their_addr.sin_addr)

检索标准 ip 表示。

【讨论】:

+1。根据平台,记得先检查家庭。可能没有要提取的 IPV4 地址... @SteveJessop 我们如何进行检查?有没有示例代码或需要寻找的东西? if (their_sockaddr_ptr->sa_family == AF_INET) struct sockaddr_in *their_inaddr_ptr = (struct sockaddr_in *)their_sockaddr_ptr; else /* not an IPv4 address */ ,或类似的。 @Emil 如何将整个 sockaddr 结构转换为 sockaddr_in?能举个例子吗? :D @mister 如果您有问题,请开始提问,这就是 SO 的用途。问题是 SO 上的一等公民。答案是,投吧! sockaddr 只是一个“抽象”地址,没有真正属于这种类型的地址,它只是定义了所有 socketaddr 结构必须具有的公共字段。 struct sockaddr * saddr = ...; if (saddr->sa_family == AF_INET) struct sockaddr_in * saddr_in = (sockaddr_in *)saddr; ... 【参考方案2】:

inet_ntoa() 适用于 IPv4; inet_ntop() 适用于 IPv4 和 IPv6。

给定输入 struct sockaddr *res,这里有两个 sn-ps 代码(在 macOS 上测试):

使用 inet_ntoa()

#include <arpa/inet.h>

struct sockaddr_in *addr_in = (struct sockaddr_in *)res;
char *s = inet_ntoa(addr_in->sin_addr);
printf("IP address: %s\n", s);

使用 inet_ntop()

#include <arpa/inet.h>
#include <stdlib.h>

// obviously INET6_ADDRSTRLEN is expected to be larger
// than INET_ADDRSTRLEN, but this may be required in case
// if for some unexpected reason IPv6 is not supported, and
// INET6_ADDRSTRLEN is defined as 0
// but this is not very likely and I am aware of no cases of
// this in practice (editor)
char s[INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN]
    = '\0';

switch(res->sa_family) 
    case AF_INET: 
        struct sockaddr_in *addr_in = (struct sockaddr_in *)res;

        ////char s[INET_ADDRSTRLEN] = '\0';
            // this is large enough to include terminating null

        inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
        break;
    
    case AF_INET6: 
        struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)res;
        
        ////char s[INET6_ADDRSTRLEN] = '\0';
            // not sure if large enough to include terminating null?

        inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
        break;
    
    default:
        break;

printf("IP address: %s\n", s);

【讨论】:

在哪个标头中? #include 不知道为什么在这里使用 malloc 和 free,静态缓冲区将是更合适的解决方案。【参考方案3】:

Emil 的回答是正确的,但我的理解是 inet_ntoa 已被弃用,而您应该使用 inet_ntop。如果您使用的是 IPv4,请将您的 struct sockaddr 转换为 sockaddr_in。您的代码将如下所示:

struct addrinfo *res;   // populated elsewhere in your code
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ipv4->sin_addr), ipAddress, INET_ADDRSTRLEN);

printf("The IP address is: %s\n", ipAddress);

查看this great resource 了解更多说明,包括如何为 IPv6 地址执行此操作。

【讨论】:

【参考方案4】:

一旦将sockaddr 转换为sockaddr_in,就会变成这样:

struct sockaddr_in 
    u_short     sin_family;
    u_short     sin_port;
    struct      in_addr sin_addr;
    char        sin_zero[8];
;

【讨论】:

这比你需要格式化为字符串的要少一些。 我们如何确保铸件安全? @eonil 所有sockaddr 的第一个成员是u_short 类型标签。【参考方案5】:

你可以使用getnameinfofor Windows和for Linux。

假设你有一个好的(即它的成员有适当的值)sockaddr* 称为pSockaddr

char clienthost[NI_MAXHOST];  //The clienthost will hold the IP address.
char clientservice[NI_MAXSERV];
int theErrorCode = getnameinfo(pSockaddr, sizeof(*pSockaddr), clienthost, sizeof(clienthost), clientservice, sizeof(clientservice), NI_NUMERICHOST|NI_NUMERICSERV);

if( theErrorCode != 0 )

    //There was an error.
    cout << gai_strerror(e1) << endl;
else
    //Print the info.
    cout << "The ip address is = " << clienthost << endl;
    cout << "The clientservice = " << clientservice << endl;

【讨论】:

【参考方案6】:

以下程序解析给定域:

$ gcc a.c

$ ./a.out google.com
AF_INET: 216.58.214.238
AF_INET6: 2a00:1450:400d:803::200e

$ ./a.out google.com af_inet
AF_INET: 216.58.214.238

a.c:

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

int
main(int argc, char *argv[])

    struct addrinfo hints, *res, *cres;
    int r;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof(hints));
    if (argc > 2)
        hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;

    r = getaddrinfo(argv[1], NULL, &hints, &res);
    if (r) 
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
        exit(EXIT_FAILURE);
    

    for (cres = res; cres; cres = cres->ai_next) 
        switch (cres->ai_family) 
        case AF_INET:
            inet_ntop(AF_INET, &((struct sockaddr_in *)cres->ai_addr)->sin_addr, s, INET6_ADDRSTRLEN);
            printf("AF_INET: %s\n", s);
            break;
        case AF_INET6:
            inet_ntop(AF_INET6, &((struct sockaddr_in6 *)cres->ai_addr)->sin6_addr, s, INET6_ADDRSTRLEN);
            printf("AF_INET6: %s\n", s);
            break;
        
    

    freeaddrinfo(res);

另一个例子可以在here找到。

【讨论】:

【参考方案7】:

sockaddr 类型转换为sockaddr_in 并使用inet_ntoa 检索ipv4

char * ip = inet_ntoa(((struct sockaddr_in *)sockaddr)->sin_addr);

【讨论】:

以上是关于从 sockaddr 结构中获取 IPV4 地址的主要内容,如果未能解决你的问题,请参考以下文章

如何从 ip 地址或 sockaddr_in 结构 (C++/WinAPI) 中获取主机名?

套接字地址结构

网络编程——sockaddr 与 sockaddr_in

套接字地址结构

sockaddr数据结构

sockaddr和sockaddr_in的区别