声明:

本文是我在工作中遇到的网络相关的问题,以及自己的一些总结,希望可以对你有所帮助。

介绍:

获得本地ipv4和ipv6的方式有两种,一种是通过调用getifaddrs函数而另一种是通过socket的ioctl获得ipv4,而通过/proc/net/if_inet6节点来获得ipv6。他们的具体实现方式为:

通过调用getifaddrs函数来获得本地ipv4和ipv6:

具体参考:https://man7.org/linux/man-pages/man3/getifaddrs.3.html

涉及的函数以及包含的头文件:

#include <sys/types.h>
#include <ifaddrs.h>
int getifaddrs(struct ifaddrs **ifap);
void freeifaddrs(struct ifaddrs *ifa);

getifaddrs函数的作用是:创建一个描述本地系统网络接口的结构链表,并将链表中第一项的地址存储在*ifap中。该列表由ifaddrs结构组成,定义如下:

struct ifaddrs {struct ifaddrs  *ifa_next;    /* Next item in list */char            *ifa_name;    /* Name of interface */unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */struct sockaddr *ifa_addr;    /* Address of interface */struct sockaddr *ifa_netmask; /* Netmask of interface */union {struct sockaddr *ifu_broadaddr;  /* Broadcast address of interface */struct sockaddr *ifu_dstaddr;   /* Point-to-point destination address */} ifa_ifu;
#define              ifa_broadaddr ifa_ifu.ifu_broadaddr
#define              ifa_dstaddr   ifa_ifu.ifu_dstaddrvoid            *ifa_data;    /* Address-specific data */
};
  • ifa_next字段包含指向列表中的下一个结构的指针,如果这是列表的最后一项,则为空。
  • ifa_name指向以空字符结束的接口名。
  • ifa_addr字段指向包含接口地址的结构。(应该参考sa_family子字段来确定地址结构的格式。)这个字段可能包含一个空指针。

而要获取的ipv4或者ipv6地址主要存放在ifa_addr中。

  具体的代码实现:

#define _GNU_SOURCE     /* To get defns of NI_MAXSERV and NI_MAXHOST */
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/if_link.h>int main(int argc, char *argv[])
{struct ifaddrs *ifaddr;int family, s;char host[NI_MAXHOST];if (getifaddrs(&ifaddr) == -1) {  //通过getifaddrs获得ifaddrs 结构体perror("getifaddrs");exit(EXIT_FAILURE);}/* Walk through linked list, maintaining head pointer so we can free list later */for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {if (ifa->ifa_addr == NULL)continue;family = ifa->ifa_addr->sa_family;  //通过family来确定包的类型/* Display interface name and family (including symbolic form of the latter for the common families) */printf("%-8s %s (%d)\n",ifa->ifa_name,(family == AF_PACKET) ? "AF_PACKET" :(family == AF_INET) ? "AF_INET" :(family == AF_INET6) ? "AF_INET6" : "???",family);/* For an AF_INET* interface address, display the address */if (family == AF_INET || family == AF_INET6) {s = getnameinfo(ifa->ifa_addr,(family == AF_INET) ? sizeof(struct sockaddr_in) :sizeof(struct sockaddr_in6),host, NI_MAXHOST,NULL, 0, NI_NUMERICHOST);if (s != 0) {printf("getnameinfo() failed: %s\n", gai_strerror(s));exit(EXIT_FAILURE);}printf("\t\taddress: <%s>\n", host);} else if (family == AF_PACKET && ifa->ifa_data != NULL) {struct rtnl_link_stats *stats = ifa->ifa_data;printf("\t\ttx_packets = %10u; rx_packets = %10u\n""\t\ttx_bytes   = %10u; rx_bytes   = %10u\n",stats->tx_packets, stats->rx_packets,stats->tx_bytes, stats->rx_bytes);}}freeifaddrs(ifaddr);exit(EXIT_SUCCESS);
}

上面代码主要分为个步骤:

  • 1. getifaddrs(&ifaddr) //通过getifaddrs获得ifaddrs 结构体
  • 2. family = ifa->ifa_addr->sa_family; //从ifaddrs 中读取sa_family,通过family来确定包的类型
  • 3. 在getnameinfo函数中,通过不用的family 来使用不用的sockaddr结构体获得不同的ipv4或者ipv6地址信息。

上面代码中使用了getnameinfo函数获取IP地址的方式,而从上面的信息中我们已经获得了ifaddrs 结构体和family ,这个时候也可以直接读ifa_addr来获得想要的信息。

上面代码对应的结果为:

通过ioctl的方式来获得ipv4同时通过/proc/net/if_inet6节点获得ipv6:

具体参考:https://domen.ipavec.net/en/get-ip-ipv6-and-mac-addresses-using-ioctl-and-procfs-linux-c/

通过/proc/net/if_inet6节点获得ipv6的方式在下面parse_inet6函数中,他的主要实现方式为:

  • 1. 通过fopen("/proc/net/if_inet6", "r");来打开节点
  • 2. 通过fscanf将读到的数据以指定的格式存放到对应的参数中,其中从/proc/net/if_inet6节点读到的格式为:

  • 因此需要通过%2hhx来限定读取数据的长度。
  • 3. 通过inet_ntop(AF_INET6, ipv6, address, sizeof(address)将读到的数字类型的ipv6地址转化为字符串类型的ip地址。

具体代码:

#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <dirent.h>#define IPV6_ADDR_GLOBAL        0x0000U
#define IPV6_ADDR_LOOPBACK      0x0010U
#define IPV6_ADDR_LINKLOCAL     0x0020U
#define IPV6_ADDR_SITELOCAL     0x0040U
#define IPV6_ADDR_COMPATv4      0x0080Uvoid parse_inet6(const char *ifname) {FILE *f;int ret, scope, prefix;unsigned char ipv6[16];char dname[IFNAMSIZ];char address[INET6_ADDRSTRLEN];char *scopestr;f = fopen("/proc/net/if_inet6", "r");if (f == NULL) {return;}while (19 == fscanf(f," %2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx %*x %x %x %*x %s",&ipv6[0],&ipv6[1],&ipv6[2],&ipv6[3],&ipv6[4],&ipv6[5],&ipv6[6],&ipv6[7],&ipv6[8],&ipv6[9],&ipv6[10],&ipv6[11],&ipv6[12],&ipv6[13],&ipv6[14],&ipv6[15],&prefix,&scope,dname)) {if (strcmp(ifname, dname) != 0) {continue;}if (inet_ntop(AF_INET6, ipv6, address, sizeof(address)) == NULL) {continue;}switch (scope) {case IPV6_ADDR_GLOBAL:scopestr = "Global";break;case IPV6_ADDR_LINKLOCAL:scopestr = "Link";break;case IPV6_ADDR_SITELOCAL:scopestr = "Site";break;case IPV6_ADDR_COMPATv4:scopestr = "Compat";break;case IPV6_ADDR_LOOPBACK:scopestr = "Host";break;default:scopestr = "Unknown";}printf("IPv6 address: %s, prefix: %d, scope: %s\n", address, prefix, scopestr);}fclose(f);
}void parse_ioctl(const char *ifname)
{int sock;struct ifreq ifr;struct sockaddr_in *ipaddr;char address[INET_ADDRSTRLEN];size_t ifnamelen;/* copy ifname to ifr object */ifnamelen = strlen(ifname);if (ifnamelen >= sizeof(ifr.ifr_name)) {return ;}memcpy(ifr.ifr_name, ifname, ifnamelen);ifr.ifr_name[ifnamelen] = '\0';/* open socket */sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);if (sock < 0) {return;}/* process mac */if (ioctl(sock, SIOCGIFHWADDR, &ifr) != -1) {printf("Mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",(unsigned char)ifr.ifr_hwaddr.sa_data[0],(unsigned char)ifr.ifr_hwaddr.sa_data[1],(unsigned char)ifr.ifr_hwaddr.sa_data[2],(unsigned char)ifr.ifr_hwaddr.sa_data[3],(unsigned char)ifr.ifr_hwaddr.sa_data[4],(unsigned char)ifr.ifr_hwaddr.sa_data[5]);}/* process mtu */if (ioctl(sock, SIOCGIFMTU, &ifr) != -1) {printf("MTU: %d\n", ifr.ifr_mtu);}/* die if cannot get address */if (ioctl(sock, SIOCGIFADDR, &ifr) == -1) {close(sock);return;}/* process ip */ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Ip address: %s\n", address);}/* try to get broadcast */if (ioctl(sock, SIOCGIFBRDADDR, &ifr) != -1) {ipaddr = (struct sockaddr_in *)&ifr.ifr_broadaddr;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Broadcast: %s\n", address);}}/* try to get mask */if (ioctl(sock, SIOCGIFNETMASK, &ifr) != -1) {ipaddr = (struct sockaddr_in *)&ifr.ifr_netmask;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Netmask: %s\n", address);}}close(sock);
}int main(void)
{DIR *d;struct dirent *de;d = opendir("/sys/class/net/");if (d == NULL) {return -1;}while (NULL != (de = readdir(d))) {if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {continue;}printf("Interface %s\n", de->d_name);parse_ioctl(de->d_name);parse_inet6(de->d_name);printf("\n");}closedir(d);return 0;
}

运行结果为:

socket的ioctl函数:

要看懂上面获得ipv4地址的代码需要了解socket以及其ioctl函数:建立socket后,通过ioctl获得各个参数的结构体。在Linux中,通过ioctl可以获得或者设置特定参数的数值,在网络中同样可以。

通过ioctl方式获得参数通常会通过两个结构体将参数返回,一个是ifconf:通过SIOCGIFCONF来获取所有接口的清单 。 而另一个为ifreq:获得其他的相关信息,具体的可以参考下ioctl选项与对应的数据类型:

参考:https://www.cnblogs.com/oxspirt/p/7478321.html

类别

Request

说明

数据类型

套接口

SIOCATMARK

SIOCSPGRP

SIOCGPGRP

是否位于带外标记

设置套接口的进程ID 或进程组ID

获取套接口的进程ID 或进程组ID

int

int

int

文件

FIONBIN

FIOASYNC

FIONREAD

FIOSETOWN

FIOGETOWN

设置/ 清除非阻塞I/O 标志

设置/ 清除信号驱动异步I/O 标志

获取接收缓存区中的字节数

设置文件的进程ID 或进程组ID

获取文件的进程ID 或进程组ID

int

int

int

int

int

接口

SIOCGIFCONF

SIOCSIFADDR

SIOCGIFADDR

SIOCSIFFLAGS

SIOCGIFFLAGS

SIOCSIFDSTADDR

SIOCGIFDSTADDR

SIOCGIFBRDADDR

SIOCSIFBRDADDR

SIOCGIFNETMASK

SIOCSIFNETMASK

SIOCGIFMETRIC

SIOCSIFMETRIC

SIOCGIFMTU

SIOCxxx

获取所有接口的清单

设置接口地址

获取接口地址

设置接口标志

获取接口标志

设置点到点地址

获取点到点地址

获取广播地址

设置广播地址

获取子网掩码

设置子网掩码

获取接口的测度

设置接口的测度

获取接口MTU

(还有很多取决于系统的实现)

struct ifconf

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

struct ifreq

ARP

SIOCSARP

SIOCGARP

SIOCDARP

创建/ 修改ARP 表项

获取ARP 表项

删除ARP 表项

struct arpreq

struct arpreq

struct arpreq

路由

SIOCADDRT

SIOCDELRT

增加路径

删除路径

struct rtentry

struct rtentry

I_xxx

而具体这两种结构体的关系为:

参考:https://developer.aliyun.com/article/244082

而两种结构体的具体表示可以参考:

https://blog.csdn.net/qq_41453285/article/details/100567095

https://segmentfault.com/a/1190000005138358

一、struct ifconf结构体

功能:用来保存 所有接口的清单

/** Structure used in SIOCGIFCONF request.* Used to retrieve interface configuration* for machine (useful for programs which* must know all networks accessible).*/
struct ifconf  {int ifc_len;            /* size of buffer   */union {char __user *ifcu_buf;struct ifreq __user *ifcu_req;} ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf       /* buffer address   */
#define ifc_req ifc_ifcu.ifcu_req       /* array of structures  */

二、struct ifreq结构体

功能:用来保存某个接口的信息

/** Interface request structure used for socket* ioctl's.  All interface ioctl's must have parameter* definitions which begin with ifr_name.  The* remainder may be interface specific.*/
struct ifreq {
#define IFHWADDRLEN 6union{char ifrn_name[IFNAMSIZ];        /* if name, e.g. "en0" */} ifr_ifrn;union {struct sockaddr ifru_addr;struct   sockaddr ifru_dstaddr;struct    sockaddr ifru_broadaddr;struct  sockaddr ifru_netmask;struct  sockaddr ifru_hwaddr;short    ifru_flags;int  ifru_ivalue;int ifru_mtu;struct  ifmap ifru_map;char    ifru_slave[IFNAMSIZ];   /* Just fits the size */char    ifru_newname[IFNAMSIZ];void __user *    ifru_data;struct    if_settings ifru_settings;} ifr_ifru;
};
#define ifr_name    ifr_ifrn.ifrn_name  /* interface name   */
#define ifr_hwaddr  ifr_ifru.ifru_hwaddr    /* MAC address      */
#define ifr_addr    ifr_ifru.ifru_addr  /* address      */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
#define ifr_broadaddr   ifr_ifru.ifru_broadaddr /* broadcast address    */
#define ifr_netmask ifr_ifru.ifru_netmask   /* interface net mask   */
#define ifr_flags   ifr_ifru.ifru_flags /* flags        */
#define ifr_metric  ifr_ifru.ifru_ivalue    /* metric       */
#define ifr_mtu     ifr_ifru.ifru_mtu   /* mtu          */
#define ifr_map     ifr_ifru.ifru_map   /* device map       */
#define ifr_slave   ifr_ifru.ifru_slave /* slave device     */
#define ifr_data    ifr_ifru.ifru_data  /* for use by interface */
#define ifr_ifindex ifr_ifru.ifru_ivalue    /* interface index  */
#define ifr_bandwidth   ifr_ifru.ifru_ivalue    /* link bandwidth   */
#define ifr_qlen    ifr_ifru.ifru_ivalue    /* Queue length     */
#define ifr_newname ifr_ifru.ifru_newname   /* New name     */
#define ifr_settings    ifr_ifru.ifru_settings  /* Device/proto settings*/

而通过ifreq 结构体我们可以清楚的获得各个接口的信息。而在之前的例子中使用了这些参数:

主要是分为两个步骤:

  • 1.建立socket连接
  • 2. 通过ioctl的各个参数来获得各个信息:
void parse_ioctl(const char *ifname)
{int sock;struct ifreq ifr;struct sockaddr_in *ipaddr;char address[INET_ADDRSTRLEN];size_t ifnamelen;/* copy ifname to ifr object */ifnamelen = strlen(ifname);if (ifnamelen >= sizeof(ifr.ifr_name)) {return ;}memcpy(ifr.ifr_name, ifname, ifnamelen);ifr.ifr_name[ifnamelen] = '\0';/* open socket */sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);if (sock < 0) {return;}/* process mac */if (ioctl(sock, SIOCGIFHWADDR, &ifr) != -1) {printf("Mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",(unsigned char)ifr.ifr_hwaddr.sa_data[0],(unsigned char)ifr.ifr_hwaddr.sa_data[1],(unsigned char)ifr.ifr_hwaddr.sa_data[2],(unsigned char)ifr.ifr_hwaddr.sa_data[3],(unsigned char)ifr.ifr_hwaddr.sa_data[4],(unsigned char)ifr.ifr_hwaddr.sa_data[5]);}/* process mtu */if (ioctl(sock, SIOCGIFMTU, &ifr) != -1) {printf("MTU: %d\n", ifr.ifr_mtu);}/* die if cannot get address */if (ioctl(sock, SIOCGIFADDR, &ifr) == -1) {close(sock);return;}/* process ip */ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Ip address: %s\n", address);}/* try to get broadcast */if (ioctl(sock, SIOCGIFBRDADDR, &ifr) != -1) {ipaddr = (struct sockaddr_in *)&ifr.ifr_broadaddr;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Broadcast: %s\n", address);}}/* try to get mask */if (ioctl(sock, SIOCGIFNETMASK, &ifr) != -1) {ipaddr = (struct sockaddr_in *)&ifr.ifr_netmask;if (inet_ntop(AF_INET, &ipaddr->sin_addr, address, sizeof(address)) != NULL) {printf("Netmask: %s\n", address);}}close(sock);
}

上面代码的运行结果为:

结构体sockaddr、sockaddr_in、sockaddr_in6之间的区别和联系:

其实对于上面的信息我们一般最关注的是地址的信息,在ifreq 中通常使用结构体sockaddr来表示socket的地址信息,但是如果需要获得详细的ipv4或者ipv6信息,我们就需要将sockaddr结构体转化为sockaddr_in或者sockaddr_in6。而结构体sockaddr、sockaddr_in、sockaddr_in6之间的区别和联系为:可以参考:

https://blog.csdn.net/albertsh/article/details/80991684

他们的定义为:

/* /usr/include/bits/socket.h */
/* Structure describing a generic socket address.  */
struct sockaddr
{__SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */char sa_data[14];           /* Address data.  */
};/* /usr/include/netinet/in.h */
/* Structure describing an Internet socket address.  */
struct sockaddr_in
{__SOCKADDR_COMMON (sin_);in_port_t sin_port;         /* Port number.  */struct in_addr sin_addr;    /* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];
};/* /usr/include/netinet/in.h */#ifndef __USE_KERNEL_IPV6_DEFS/* Ditto, for IPv6.  */
struct sockaddr_in6
{__SOCKADDR_COMMON (sin6_);in_port_t sin6_port;        /* Transport layer port # */uint32_t sin6_flowinfo;     /* IPv6 flow information */struct in6_addr sin6_addr;  /* IPv6 address */uint32_t sin6_scope_id;     /* IPv6 scope-id */
};#endif /* !__USE_KERNEL_IPV6_DEFS */

在sockaddr_in中ipv4的地址存放在sin_addr中,端口号存放在sin_port中。而对于ipv6 ,sockaddr_in6的sin6_addr存放地址,sin6_port存放端口号。所以主要的区别就是sin_addr和sin6_addr以及sin_port和sin6_port的区别

而查看代码发现sin_addr和sin_port的定义为:

/* Type to represent a port.  */
typedef uint16_t in_port_t; /* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
};而sin6_addr和sin6_port的定义为:
#ifndef __USE_KERNEL_IPV6_DEFS
/* IPv6 address */
struct in6_addr
{union{uint8_t __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNUuint16_t __u6_addr16[8];uint32_t __u6_addr32[4];
#endif} __in6_u;
#define s6_addr         __in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16      __in6_u.__u6_addr16
# define s6_addr32      __in6_u.__u6_addr32
#endif
};
#endif /* !__USE_KERNEL_IPV6_DEFS */

从上面的定义可以看出,ipv4的ip地址存放在32位无符号整形中,而ipv6的地址时可以变化的,可以将他们存放到不同的容器中。所以也就可以通过不同的方式读出来了。

因此对于同样的IP地址,我们可以使用不同的方式写出,测试代码为:

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main() {struct in6_addr ip;char *addr1, *addr2, *addr3, *addr4, *addr5;char ipv6_str[128];addr1 = strdup("2a01:198:603:0:396e:4789:8e99:890f");inet_pton(AF_INET6, addr1, &ip);printf("2a01:198:603:0:396e:4789:8e99:890f \n");printf("0x%4x %4x %4x %4x %4x %4x %4x %4x\n",htons(ip.s6_addr16[0]),htons(ip.s6_addr16[1]),htons(ip.s6_addr16[2]),htons(ip.s6_addr16[3]),htons(ip.s6_addr16[4]),htons(ip.s6_addr16[5]),htons(ip.s6_addr16[6]),htons(ip.s6_addr16[7]));printf("xiang    %8x %8x %8x %8x \n",htonl(ip.s6_addr32[0]),htonl(ip.s6_addr32[1]),htonl(ip.s6_addr32[2]),htonl(ip.s6_addr32[3]));return 0;
}   

运行结果为:

地址转换函数inet_addr(), inet_aton(), inet_ntoa()和inet_ntop(), inet_pton():

在上面的代码中使用了inet_pton函数来将字符串形式的IP地址转化为数字类型的,而同时也可以使用inet_ntop函数将数字类型的IP地址转化为字符串类型,下面介绍地址转换函数inet_addr(), inet_aton(), inet_ntoa()和inet_ntop(), inet_pton()

参考:http://haoyuanliu.github.io/2017/01/15/%E5%9C%B0%E5%9D%80%E8%BD%AC%E6%8D%A2%E5%87%BD%E6%95%B0inet-addr-inet-aton-inet-ntoa-%E5%92%8Cinet-ntop-inet-pton/

inet_addr()函数

功能:inet_addr()函数用于将点分十进制IP地址转换成网络字节序IP地址;

原型:in_addr_t inet_addr(const char *cp);

返回值:如果正确执行将返回一个无符号长整数型数。如果传入的字符串不是一个合法的IP地址,将返回INADDR_NONE;

头文件:arpa/inet.h (Linux)

inet_aton()函数

功能:inet_aton()函数用于将点分十进制IP地址转换成网络字节序IP地址;

原型:int inet_aton(const char *string, struct in_addr *addr);

返回值:如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零;

头文件:sys/socket.h (Linux)

inet_ntoa()函数

功能inet_ntoa()函数用于网络字节序IP转化点分十进制IP;

原型:char *inet_ntoa (struct in_addr);

返回值:若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来;

头文件:arpa/inet.h (Linux)

inet_ntop()和inet_pton()函数

inet_ntop()函数

功能:inet_ntop()函数用于将网络字节序的二进制地址转换成文本字符串;

原型:const char *inet_pton(int domain, const void *restrict addr, char *restrict str, socklen_t size);

返回值:若成功,返回地址字符串指针;若出错,返回NULL;

头文件:arpa/inet.h (Linux)

inet_pton()函数

功能:inet_pton()函数用于将文本字符串格式转换成网络字节序二进制地址;

原型:int inet_pton(int domain, const char *restrict str, void *restrict addr);

返回值:若成功,返回1;若格式无效,返回0;若出错,返回-1;

头文件:arpa/inet.h (Linux)

在下面的例子中列举了inet_ntop()和inet_pton()的转化:

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int main() {struct in6_addr ip;char *addr1, *addr2, *addr3, *addr4, *addr5;char ipv6_str[128];addr1 = strdup("2a01:198:603:0:396e:4789:8e99:890f");addr2 = strdup("2a01:198:603:0::");addr3 = strdup("2a01::");addr4 = strdup("2a01::2a01");addr5 = strdup("::2a01");inet_pton(AF_INET6, addr1, &ip);printf("2a01:198:603:0:396e:4789:8e99:890f \n");printf("0x%4x %4x %4x %4x %4x %4x %4x %4x\n",htons(ip.s6_addr16[0]),htons(ip.s6_addr16[1]),htons(ip.s6_addr16[2]),htons(ip.s6_addr16[3]),htons(ip.s6_addr16[4]),htons(ip.s6_addr16[5]),htons(ip.s6_addr16[6]),htons(ip.s6_addr16[7]));printf("xiang    %8x %8x %8x %8x \n",htonl(ip.s6_addr32[0]),htonl(ip.s6_addr32[1]),htonl(ip.s6_addr32[2]),htonl(ip.s6_addr32[3]));printf("0x%4x %4x %4x %4x %4x %4x %4x %4x\n",ip.s6_addr16[0],ip.s6_addr16[1],(ip.s6_addr16[2]),(ip.s6_addr16[3]),(ip.s6_addr16[4]),(ip.s6_addr16[5]),(ip.s6_addr16[6]),(ip.s6_addr16[7]));int i = 0;unsigned int ipv6_in32[4] = {0};unsigned int ipv6_in32_htonl[4] = {0};for (i=0;i<4;i++) {printf("333  0x%8x\n",ip.s6_addr32[i]);ipv6_in32[i] =  ip.s6_addr32[i];        }struct in6_addr ipv6_s_32;i = 0;for (i=0;i<4;i++) {ipv6_s_32.s6_addr32[i] = 0;ipv6_in32_htonl[i] = htonl(ip.s6_addr32[i]) & 0xffffffff;printf("000  0x%16x\n",htonl(ip.s6_addr32[i]) & 0xffffffff );ipv6_s_32.s6_addr32[i] |= ((unsigned int)ip.s6_addr32[i]) & 0xffffffff;printf("000_ntohl  0x%16x\n",ntohl(ipv6_in32_htonl[i]) & 0xffffffff );}if(inet_ntop(AF_INET6,&ipv6_s_32,ipv6_str,40)==NULL) /*地址由二进制数转换为点分十进制*/{printf("fail to convert");}printf("ipv6_str %s \n",ipv6_str);return 0;
}

运行结果为:

主机字节序与网络字节序的转化:

通过上面对于htonl和ntohl运行结果的不同可以知道htonl和ntohl的作用是32位的主机字节序与网络字节序的转化:

参考:https://blog.csdn.net/zhangdawei5A504/article/details/45747937

由于不同的系统会有不同的模式,为了统一,规定在网络传输中使用大端模式,这就是网络字节序。而当我们的主机字节与网络字节有所不用的时候就需要使用下面的函数进行转化了:

uint32_t htonl(uint32_t hostlong);//32位的主机字节序转换到网络字节序
uint16_t htons(uint16_t hostshort);//16位的主机字节序转换到网络字节序
uint32_t ntohl(uint32_t netlong);//32位的网络字节序转换到主机字节序
uint16_t ntohs(uint16_t netshort);//16位的网络字节序转换到主机字节序

拿htonl和ntohl来分析,htonl函数的内部实现原理是这样,先判断主机是什么模式存储,如果是大端模式,就跟网络字节序一致,直接返回参数即可,如果是小端模式,则把形参转换成大端模式存储在一个临时参数内,再把临时参数返回;而ntohl函数的实现原理也是一样的过程,但是要注意它的参数,参数是网络字节序,就是大端模式存储,而不管你传入实参的过程是如果存储的,因此当判断主机是大端模式的时候,会直接返回,因为该函数默认会认为形参是网络字节序,把它当大端模式来看,如果判断主机是小端模式,就会将实参做转换,转换的过程并不复杂,就是逆序存储各个字节的数据,所以结果就被转换。

获取本地ipv4和ipv6信息相关推荐

  1. 获取本地计算机的网络信息

    Windows网络编程第六章探测网络中的在线设备 实验:获取本地计算机的网络信息 实验分析:主要练习对GetAdaptersInfo().GetNetworkParams().GetInterface ...

  2. C# 获取本地IP信息

    实例地址:C#(WPF)获取本地IP地址C#编程-C#文档类资源-CSDN下载 获取正在使用IP: using System.Net; using System.Net.NetworkInformat ...

  3. Linux下编程获取本地IP地址的常见方法

    代码编译运行平台:Linux 64bits+g++(-m64),-m64表示生成64bits的程序. 在进行Linux网络编程时,经常用到本机IP地址.本文罗列一下常见方法,以备不时之需. 获取本机I ...

  4. C++在windows下获取本地主机ipv4地址和ipv6地址的代码

    把内容过程中经常用的内容段记录起来,下面内容段是关于C++在windows下获取本地主机ipv4地址和ipv6地址的内容,应该是对码农有所用处. #include <Winsock2.h> ...

  5. QT获取本地网络信息

    QT获取本地网络信息 开发工具与关键技术:QtCreator.C++ 作者:何任贤 撰写时间:2020年06月10日 获取本机网络信息要在工程文件(.Pro)加上 QT += network 然后就是 ...

  6. 搭建IPv6签到服务器,并使用FRP获取IPv6信息

    一.目录说明 不同平台的FRP程序可在GitHub - fatedier/frp: A fast reverse proxy to help you expose a local server beh ...

  7. IP.SB - 在线 IPv4 / IPv6 信息查询

    IP.SB - 在线 IPv4 / IPv6 信息查询 1.IP.SB 的由来 IP.SB 前身是 IP.GS ,一个专门用于查询本地出口 IP 的网站,初衷是为了让用户更方便地知道自己的本地出口 I ...

  8. 【Qt】获取本地IP(IPv4)

    1.问题描述 获取本地IP列表有"127.0.0.1".IPv4.IPv6等,一般使用IPv4,如何从已经获取的IP列表中挑出IPv4. 2.解决方法 QString ipv4; ...

  9. linux c 通过套接字获取本地远程地址信息 getsockname getpeername 简介

    getsockname 函数用于获取与某个套接字关联的本地协议地址  getpeername 函数用于获取与某个套接字关联的外地协议地址 定义如下: #include<sys/socket.h& ...

最新文章

  1. 爬虫python爬取页面请求_Python网络爬虫第三弹《爬取get请求的页面数据》
  2. JSP复习题【侵权联系我删除】
  3. 好奇,我们常用的 Integer 内部为什么会去实现 Comparable 接口,他的作用是什么?...
  4. Power Platform之Power Automate新增RPA功能
  5. tensorflow出现 ImportError: DLL load failed: 找不到指定的程序
  6. 近数据处理(NDP)——GaussDB(for MySQL)性能提升的秘密
  7. 刀片存储助力发挥融合基础架构优势
  8. r语言保存成html文件,R语言统计结果输出至本地文件的几种方法示例
  9. php实现读写ic卡,diy用PIC单片机实现的IC卡读写器
  10. (没有ignore选项时)安装MongoDB4.0以上版本出现 Verify that you have sufficient privileges to start system services
  11. 如何减小电压跟随器输出电阻_运算放大器和比较器还傻傻分不清楚?一篇图文教你轻松辨认...
  12. autocad ios 虚线_autocad 如何画虚线
  13. 深度学习论文: Real-time Semantic Segmentation via Spatial-detail Guided Context Propagation
  14. TI CC1310 sub1G的SDK开发之入门
  15. ZoomIt v4.5
  16. Python入门刷题第三天(类和对象和继承)
  17. H5 会动的皮卡丘动画
  18. 直角三角形面积Java_用java编写输出直角三角型、倒直角三角形
  19. 二叉树的深度(前序 中序 后序 递归非递归搜素)、广度、搜索 C++
  20. 将word文档中的图片批量导出到文件夹中的办法

热门文章

  1. rancher证书过期怎么办
  2. java swt griddata_SWT的GridData中一个需要注意的地方
  3. 如何快速完善SOLIDWORKS文件属性信息
  4. 五、神经网络过拟合处理方法(一)
  5. 把大象装冰箱,要几个步骤?
  6. 学籍管理系统+python mysql+hash加密
  7. Android BLE设备蓝牙通信框架BluetoothKit
  8. 行为驱动:python+behave,学习记录
  9. drop table命令
  10. 魔兽世界服务器维护拍卖行时间,《魔兽世界》开放剩余分钟 兑换游戏天数和点数服务...