1、地址转换

1、网路地址转化函数inet_pton和inet_ntop
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。


#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);     //将点分十进制的ip地址转化为用于网络传输的数值格式
        返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
 
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);     //将数值格式转化为点分十进制的ip地址格式

        返回值:若成功则为指向结构的指针,若出错则为NULL

(1)这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
(2)第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0.

(3)inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。

int getsockname(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);

getsockname可以获得一个与sockfd相关的本地地址。

getpeername可以获得一个与sockfd相关的对端地址。

2、地址格式

sa_family_t 通常为8位无符号整数

in_port_t TCP或UDP端口,一般为uint16_t

sockaddr和sockaddr_in开头一样,内存大小也一样,可以互相转换。

在使用bind或accept时,虽然参数为struct sockaddr *addr,但是可以取sockaddr_in6类型的地址,通过传递地址和长度socklen_t  addrlen来进行处理。如果是IPV6操作,则socklen_t  addrlen长度为sizeof(struct sockaddr_in6)。在使用inet_pton和inet_ntop时也有类似问题。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
typedef unsigned short int sa_family_t

/* Structure describing a generic socket address.  */
struct sockaddr
{
 uint8_t sa_len;
 sa_family_t sa_family;    /* 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
{
 uint8_t sin_len;
 sa_family_t sin_family; 
 in_port_t sin_port;         /* Port number.  */
 struct in_addr sin_addr;    /* Internet address.  */

 /* Pad to size of `struct sockaddr'.  */
 unsigned char sin_zero[8];
};

typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };



struct sockaddr_in6
{
 uint8_t sin6_len;
 sa_family_t sin6_family; 
 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 */
};

struct in6_addr
  {
	uint8_t	s6_addr[16];
  };

3、IPV6绑定时报错处理

local_addr和local_port确定本地的IPV6地址和端口号,在bind时报错,检查IPV6地址和端口号均无问题,后来检查发现绑定IPV6地址时,需要指定sin6_scope_id,可以通过if_nametoindex查询到端口名称对应的索引。

Cannot bind socket (Cannot assign requested address)
 

struct sockaddr_in6 src;
int sock;
int opt = 1;
struct in6_addr ipv6_inaddr;
bzero(&src, sizeof(src));


if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) <= 0)
{
	printf("open Socket error (%s)\n", strerror(errno));
    return 0;
}
  
src.sin6_family = AF_INET6;
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(ret == -1)
{
    printf("setsockopt fail %s\n", strerror(errno));
    return 0;
}
  
/* bind socket */
src.sin6_family = AF_INET6;
src.sin6_port = htons((unsigned short) local_port); /* short, network byte order */

if(inet_pton(AF_INET6,local_addr,(void *)&ipv6_inaddr)!=1)
{
    printf("Cannot conver local_addr %s, error %s\n", local_addr, strerror(errno));
    src.sin6_addr = in6addr_any;  //
}
else
{
    src.sin6_addr = ipv6_inaddr;
}

src.sin6_scope_id = interface;
  
if (bind(sock, (struct sockaddr *) &src, sizeof(struct sockaddr_in6)) == -1)
{
    printf("Cannot bind socket (%s)\n", strerror(errno));	
    return 0;
}
#include <net/if.h>

unsigned if_nametoindex(const char *ifname);
char *if_indextoname(unsigned ifindex, char *ifname);
struct if_nameindex *if_nameindex(void);
void if_freenameindex(struct if_nameindex *ptr);

/*
if_nametoindex():指定网络接口名称字符串作为参数;若该接口存在,则返回相应的索引,否则返回0

if_indextoname():指定网络接口索引以及一块长度至少为IF_NAMESIZE(16)字节的内存区域作为参数;若索引对应的网络接口存在,则在内存区域中返回该接口的名称字符串,否则返回NULL,并将errno设置为相应的值

if_nameindex():返回动态分配的struct if_nameindex结构数组,数组中的每一个元素分别对应一个本地网络接口;struct if_nameindex结构的if_index字段为接口索引,if_name字段为接口名称字符串;索引为0且名称字符串为NULL表示结构数组的末尾;调用出错时,返回NULL,并将errno设置为相应的值

if_freenameindex():通过if_nameindex()获取完毕接口名称与索引后,调用该函数以释放动态分配的内存区域

以上4个函数在系统的man文档中都可以查看相应的描述,且都是POSIX标准支持的,Linux内核可能未实现这些函数,或已实现但不同于POSIX标准。这些函数的原型声明与定义并未出现在CentOS 6.7的定制内核2.6.32-573.26.1.el6.x86_64以及原版内核2.6.32.5中,而是由系统的glibc-2.12实现:在glibc-2.12.2源码树中,函数的原型声明位于sysdeps/gnu/net/if.h与sysdeps/generic/net/if.h,函数的定义位于sysdeps/unix/sysv/linux/if_index.c中,本质上是对ioctl(2)的SIOCGIFNAME,SIOCGIFCONF,SIOCGIFINDEX等操作以及netlink套接字进行了封装
*/

 


版权声明:本文为bluewhu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/bluewhu/article/details/128344618