inet_pton()函数二进制文件在哪里?

时间:2017-08-15 08:35:05

标签: c ip ip-address

我想将IP地址转换为二进制文件,以便将它们存储在MySQL DB中。 在线搜索说这是存储地址的最有效方式,因为ipv4适合4个字节,ipv6适合16个字节(What is the ideal datatype to store IP address in a mysql table?)。

现在,似乎常见的方法是使用inet_pton()函数,该函数说:" inet_pton - 将IPv4和IPv6地址从文本转换为二进制形式" (http://man7.org/linux/man-pages/man3/inet_pton.3.html

所以我的问题是二进制数存储在哪里?

我使用" sockaddr_in"大多数在线指南建议使用inet_pton结构,这就是结构的样子:

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};

struct in_addr {
    unsigned long s_addr;  // load with inet_ntop()
};

我的代码基本上是:

#include <arpa/inet.h>

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

    if (argc >= 2) {

        char *ip = malloc(strlen(argv[1]) + 1);
        memcpy(ip, argv[1], strlen(argv[1]) + 1);

        struct sockaddr_in sa;
        char str[INET_ADDRSTRLEN];

        // store this IP address in sa:
        inet_pton(AF_INET, ip, &sa.sin_addr);


        /* for verifying purposes, I'm trying to print the result
           to make sure I get a binary number (obviously, "%d" should
           not print a binary yet I do get an int result) */
        printf("%d\n", sa.sin_addr.s_addr);

    }
}

我得到的输出(使用127.0.0.1作为输入)是:16777343&lt; - 这看起来不像二进制数,如果它实际上是printf也不应该打印二进制数。如果inet_pton()将IP转换为二进制,那么该二进制文件在哪里。

如果可能,我更愿意提供一个解决方案,包括printf打印二进制文件以验证结果(但这只是个人偏好)。

修改

我的问题不是关于如何将int转换为二进制,而是关于inet_pton函数输出。我希望在答案中包含一个将int转换为二进制的机制作为奖励,但这绝不是问题的主题 - 因此它与Print an int in binary representation using C不重复:@ {3}} Andre Kampling在评论中建议

3 个答案:

答案 0 :(得分:2)

  

我得到的输出(使用127.0.0.1作为输入)是:16777343&lt; - 这似乎不是二进制数

如前所述,所有内容都存储在计算机中的“ binary ”中。 %d的{​​{1}}转换说明符只是将位模式解释为数字。

  

如果有可能,我更愿意找到一个解决方案来包含打印二进制文件的printf以验证结果(但这只是个人偏好)。

printf()中没有“二进制数”的转换说明符,因此您必须编写自己的实现。例如。对于printf(),它看起来像这样:

int

“奇怪”宏用于可移植地确定#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \ + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3)) // use unsigned because we don't want to treat a sign bit specially void print_binary(unsigned int i) { unsigned mask = 1 << (IMAX_BITS((unsigned)-1) - 1); while (mask) { putchar(i & mask ? '1' : '0'); mask >>= 1; } } 中的位数,有关详细信息,请参阅this answer

答案 1 :(得分:1)

  

[...]二进制文件在哪里?

inet_pton(AF_INET, ip, &sa.sin_addr);

获取ip指向的内容,将其转换为IP地址的二进制表示形式,然后将结果存储在sa.sin_addr的地址下。

sa.sin_addr的类型为struct in_addr,后者又定义为

struct in_addr {
  unsigned long s_addr;  
};

实际上只不过是unsigned long

unsigned long(以及所有其他整数/浮点数据类型)按概念将其值存储为二进制。您无需验证此...; - )

因此,要将上述转换的结果存储到SQL数据库中,您需要一个能够存储至少unsigned long的SQL列类型。

要将上述转换的结果传递给将其存储到数据库中的任何函数,请传递

 sa.sin_addr

 sa.sin_addr.s_addr

答案 2 :(得分:0)

为了支持IPv4和IPv6地址:

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

// Convert IPv4 or IPv6 to 16-bytes binary
int ip_to_binary (const char *ip, void *binary_16_bytes)
{
    struct sockaddr_in sa4;
    struct sockaddr_in6 sa6;

    if (inet_pton (AF_INET, ip, &sa4.sin_addr) == 1) {
        // This is a valid IPv4 address - copy 4 bytes to the end of binary
        memset (binary_16_bytes, 0, 12);
        memcpy (binary_16_bytes + 12, &sa4.sin_addr, 4);
    } else if (inet_pton (AF_INET6, ip, &sa6.sin6_addr) == 1) {
        // This is a valid IPv6 address
        memcpy (binary_16_bytes, &sa6.sin6_addr, 16);
    } else {
        // This is crap...
        return -1;
    }

    return 0;
}

// Convert 16-bytes binary to IPv4 or IPv6 address
int binary_to_ip (const void *binary_16_bytes, char *buf, size_t buflen)
{
    static uint8_t zero[16] = {0};
    short family;
    const void *sin_addr;

    // If first 12 bytes (but not all 16 bytes) are zeroes, then treat it as IPv4 address
    if (!memcmp (binary_16_bytes, zero, 12) && memcmp (binary_16_bytes, zero, 16)) {
        family = AF_INET;
        sin_addr = binary_16_bytes + 12;
    } else {
        family = AF_INET6;
        sin_addr = binary_16_bytes;
    }

    if (inet_ntop (family, sin_addr, buf, buflen) != NULL) {
        return 0;
    } else {
        return -1;
    }
}

int main (int argc, char *argv[])
{
    uint8_t binary[16];

    if (argc >= 2) {

        // IP address to binary
        if (ip_to_binary (argv[1], binary) != 0) {
            printf ("Invalid IP address!\n");
            exit (1);
        }

        // Binary back to IP address
        char buf[INET6_ADDRSTRLEN];
        if (binary_to_ip (binary, buf, sizeof (buf)) == 0) {
            printf ("IP address: %s\n", buf);
        }

    }
}

然后您可以通过执行以下操作将此缓冲区绑定到语句:(不确定,我还没有使用MySQL C API)

// ...
MYSQL_BIND bind;
unsigned long len = sizeof (binary);
bind.buffer_type = MYSQL_TYPE_BLOB;
bind.buffer = binary;
bind.buffer_length = len;
bind.is_null = 0;
bind.length = &len;
// ...