C语言 inet_pton() 函数二进制在哪里?



我想将IP地址转换为二进制,以便将它们存储在MySQL DB中。 在线搜索说这是存储地址的最有效方式,因为 ipv4 适合 4 个字节,ipv6 适合 16 个字节(在 mysql 表中存储 IP 地址的理想数据类型是什么?

现在,似乎常用的方法是使用 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("%dn", sa.sin_addr.s_addr);
}
}

我得到的输出(使用 127.0.0.1 作为输入(是:16777343 <- 这似乎不像二进制数,如果实际上是二进制数,printf 也不应该打印二进制。如果 inet_pton(( 将 IP 转换为二进制,那么该二进制在哪里。

如果可能的话,我更喜欢一个解决方案,包括打印二进制文件以验证结果的 printf(但这只是个人喜好(。

编辑

我的问题不是关于如何将 int 转换为二进制,而是关于inet_pton函数输出。我想在答案中包含一种将 int 转换为二进制的机制作为奖励,但这绝对不是问题的主题 - 因此它不是重复的: 使用 C 以二进制表示形式打印一个 int @Andre Kampling 在评论中建议

我得到的输出(使用 127.0.0.1 作为输入(是: 16777343 <-这似乎不像一个二进制数

如前所述,所有内容都">二进制"存储在计算机中。printf()%d转换说明符只是将位模式解释为数字。

如果可能的话,我更喜欢一个解决方案,包括打印二进制文件以验证结果的 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;
}
}

"奇怪"宏用于便携式确定unsigned int中的位数,有关更多信息,请参阅此答案。

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

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

为了同时支持 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: %sn", 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;
// ...

最新更新