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

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

【问题讨论】:

    标签: c ip-address


    【解决方案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 Windowsfor 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);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-21
            • 2010-11-06
            • 2017-04-16
            • 1970-01-01
            • 1970-01-01
            • 2013-06-21
            • 2015-07-24
            相关资源
            最近更新 更多