sockaddr 和sockaddr_in6的转换问题

mengmingtao 2011-11-02 06:14:31
几个结构体定义是这样的:
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};


struct sockaddr_in {
sa_family_t sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */

/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};


struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6 */
__u16 sin6_port; /* Transport layer port # */
__u32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
__u32 sin6_scope_id; /* scope id (new in RFC2553) */
};

sockaddr_in同sockaddr 相互转换好理解,
我迷惑的是,sockaddr 和sockaddr_in6所占的位数不同,大小不一样,为什么能相互转换?
...全文
2411 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
KevinHo 2014-05-20
  • 打赏
  • 举报
回复
引用 7 楼 jedihy 的回复:

#include <iostream>
struct sockaddr {
    uint8_t sa_family;        /* address family, AF_xxx        */
    char    sa_data[14];        /* 14 bytes of protocol address        */
};

struct in6_addr {
	union {
		char   __u6_addr8[16];
	} __u6_addr;			/* 128-bit IP6 address */
};

struct sockaddr_in6 {
    unsigned short int  sin6_family;    /* AF_INET6 */
    uint16_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;  /* scope id (new in RFC2553) */
};


int main(int argc, const char * argv[])
{
    
    struct sockaddr_in6 sk_in6;
    std::cout<<"size of sin6_family = "<<sizeof(sk_in6.sin6_family)<<std::endl;
    for(int i = 0;i < 16;++i){
        sk_in6.sin6_addr.__u6_addr.__u6_addr8[i] = '3';
    }
    for(int i = 0;i < 16;++i){
        std::cout<<sk_in6.sin6_addr.__u6_addr.__u6_addr8[i]<<std::endl;
    }
    std::cout<<"---------"<<std::endl;
    struct sockaddr *_sockaddr = (sockaddr*)&sk_in6;
    struct in6_addr *_in6_addr = (in6_addr*)&_sockaddr->sa_data[7];
    for(int i = 0;i < 16;++i){
        std::cout<<_in6_addr->__u6_addr.__u6_addr8[i]<<std::endl;
    }
    return 0;
}
特意写了这么一段代码来说明这个问题,相信看完就差不多明白为什么14字节能装下16字节的地址。起始根本不是装下的,只是能通过sa_data[7]来取到v6地址的首地址,即偏移量。有了偏移量就可以转回来。AF_INET6这个参数传进去之后,会用V6的逻辑来处理sockaddr这个通用套接字地址结构体。
一个小问题,应该是struct in6_addr *_in6_addr = (in6_addr*)&_sockaddr->sa_data[6];吧?不是7,是6
jedihy 2014-01-22
  • 打赏
  • 举报
回复
您说的这个确实是新标准里面推荐来做v4/v6兼容地址结构体的,但并不是只能用这个,原来的sockaddr结构体也是可以给v6用的,不存在存不下的道理。
引用 6 楼 DdruidC 的回复:
[Quote=引用 3 楼 qq120848369 的回复:] 引用 2 楼 mengmingtao 的回复: 但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个 引用 1 楼 qq120848369 的回复: struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_fa…… [/Quote] 我觉得这位说的不对 应该是系统定义了一个新的结构体来处理ipv6的sockaddr_in6结构,如下: // General socket address holding structure, big enough to hold either // struct sockaddr_in or struct sockaddr_in6 data: struct sockaddr_storage { sa_family_t ss_family; // address family // all this is padding, implementation specific, ignore it: char __ss_pad1[_SS_PAD1SIZE]; int64_t __ss_align; char __ss_pad2[_SS_PAD2SIZE]; }; 解释: In memory, the struct sockaddr_in and struct sockaddr_in6 share the same beginning structure as struct sockaddr, and you can freely cast the pointer of one type to the other without any harm, except the possible end of the universe. Just kidding on that end-of-the-universe thing...if the universe does end when you cast a struct sockaddr_in* to a struct sockaddr*, I promise you it's pure coincidence and you shouldn't even worry about it. So, with that in mind, remember that whenever a function says it takes a struct sockaddr* you can cast your struct sockaddr_in*, struct sockaddr_in6*, or struct sockadd_storage* to that type with ease and safety. struct sockaddr_storage is a struct you can pass to accept() or recvfrom() when you're trying to write IP version-agnostic code and you don't know if the new address is going to be IPv4 or IPv6. The struct sockaddr_storage structure is large enough to hold both types, unlike the original small struct sockaddr. 例子 Example // IPv4: struct sockaddr_in ip4addr; int s; ip4addr.sin_family = AF_INET; ip4addr.sin_port = htons(3490); inet_pton(AF_INET, "10.0.0.1", &ip4addr.sin_addr); s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)&ip4addr, sizeof ip4addr); // IPv6: struct sockaddr_in6 ip6addr; int s; ip6addr.sin6_family = AF_INET6; ip6addr.sin6_port = htons(4950); inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &ip6addr.sin6_addr); s = socket(PF_INET6, SOCK_STREAM, 0); bind(s, (struct sockaddr*)&ip6addr, sizeof ip6addr);
jedihy 2014-01-22
  • 打赏
  • 举报
回复
引用 6 楼 DdruidC 的回复:
[Quote=引用 3 楼 qq120848369 的回复:] 引用 2 楼 mengmingtao 的回复: 但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个 引用 1 楼 qq120848369 的回复: struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_fa…… [/Quote] 我觉得这位说的不对 应该是系统定义了一个新的结构体来处理ipv6的sockaddr_in6结构,如下: // General socket address holding structure, big enough to hold either // struct sockaddr_in or struct sockaddr_in6 data: struct sockaddr_storage { sa_family_t ss_family; // address family // all this is padding, implementation specific, ignore it: char __ss_pad1[_SS_PAD1SIZE]; int64_t __ss_align; char __ss_pad2[_SS_PAD2SIZE]; }; 解释: In memory, the struct sockaddr_in and struct sockaddr_in6 share the same beginning structure as struct sockaddr, and you can freely cast the pointer of one type to the other without any harm, except the possible end of the universe. Just kidding on that end-of-the-universe thing...if the universe does end when you cast a struct sockaddr_in* to a struct sockaddr*, I promise you it's pure coincidence and you shouldn't even worry about it. So, with that in mind, remember that whenever a function says it takes a struct sockaddr* you can cast your struct sockaddr_in*, struct sockaddr_in6*, or struct sockadd_storage* to that type with ease and safety. struct sockaddr_storage is a struct you can pass to accept() or recvfrom() when you're trying to write IP version-agnostic code and you don't know if the new address is going to be IPv4 or IPv6. The struct sockaddr_storage structure is large enough to hold both types, unlike the original small struct sockaddr. 例子 Example // IPv4: struct sockaddr_in ip4addr; int s; ip4addr.sin_family = AF_INET; ip4addr.sin_port = htons(3490); inet_pton(AF_INET, "10.0.0.1", &ip4addr.sin_addr); s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)&ip4addr, sizeof ip4addr); // IPv6: struct sockaddr_in6 ip6addr; int s; ip6addr.sin6_family = AF_INET6; ip6addr.sin6_port = htons(4950); inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &ip6addr.sin6_addr); s = socket(PF_INET6, SOCK_STREAM, 0); bind(s, (struct sockaddr*)&ip6addr, sizeof ip6addr);
您说的这个确实是新标准里面推荐来做v4/v6兼容地址结构体的,但并不是只能用这个,原来的sockaddr结构体也是可以给v6用的,不存在存不下的道理。
jedihy 2014-01-22
  • 打赏
  • 举报
回复

#include <iostream>
struct sockaddr {
    uint8_t sa_family;        /* address family, AF_xxx        */
    char    sa_data[14];        /* 14 bytes of protocol address        */
};

struct in6_addr {
	union {
		char   __u6_addr8[16];
	} __u6_addr;			/* 128-bit IP6 address */
};

struct sockaddr_in6 {
    unsigned short int  sin6_family;    /* AF_INET6 */
    uint16_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;  /* scope id (new in RFC2553) */
};


int main(int argc, const char * argv[])
{
    
    struct sockaddr_in6 sk_in6;
    std::cout<<"size of sin6_family = "<<sizeof(sk_in6.sin6_family)<<std::endl;
    for(int i = 0;i < 16;++i){
        sk_in6.sin6_addr.__u6_addr.__u6_addr8[i] = '3';
    }
    for(int i = 0;i < 16;++i){
        std::cout<<sk_in6.sin6_addr.__u6_addr.__u6_addr8[i]<<std::endl;
    }
    std::cout<<"---------"<<std::endl;
    struct sockaddr *_sockaddr = (sockaddr*)&sk_in6;
    struct in6_addr *_in6_addr = (in6_addr*)&_sockaddr->sa_data[7];
    for(int i = 0;i < 16;++i){
        std::cout<<_in6_addr->__u6_addr.__u6_addr8[i]<<std::endl;
    }
    return 0;
}
特意写了这么一段代码来说明这个问题,相信看完就差不多明白为什么14字节能装下16字节的地址。起始根本不是装下的,只是能通过sa_data[7]来取到v6地址的首地址,即偏移量。有了偏移量就可以转回来。AF_INET6这个参数传进去之后,会用V6的逻辑来处理sockaddr这个通用套接字地址结构体。
DdruidC 2011-11-28
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 qq120848369 的回复:]

引用 2 楼 mengmingtao 的回复:

但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个
引用 1 楼 qq120848369 的回复:

struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_fa……
[/Quote]

我觉得这位说的不对
应该是系统定义了一个新的结构体来处理ipv6的sockaddr_in6结构,如下:

// General socket address holding structure, big enough to hold either
// struct sockaddr_in or struct sockaddr_in6 data:

struct sockaddr_storage {
sa_family_t ss_family; // address family

// all this is padding, implementation specific, ignore it:
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};

解释:

In memory, the struct sockaddr_in and struct sockaddr_in6 share the same beginning structure as struct sockaddr, and you can freely cast the pointer of one type to the other without any harm, except the possible end of the universe.

Just kidding on that end-of-the-universe thing...if the universe does end when you cast a struct sockaddr_in* to a struct sockaddr*, I promise you it's pure coincidence and you shouldn't even worry about it.

So, with that in mind, remember that whenever a function says it takes a struct sockaddr* you can cast your struct sockaddr_in*, struct sockaddr_in6*, or struct sockadd_storage* to that type with ease and safety.

struct sockaddr_storage is a struct you can pass to accept() or recvfrom() when you're trying to write IP version-agnostic code and you don't know if the new address is going to be IPv4 or IPv6. The struct sockaddr_storage structure is large enough to hold both types, unlike the original small struct sockaddr.

例子
Example
// IPv4:

struct sockaddr_in ip4addr;
int s;

ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(3490);
inet_pton(AF_INET, "10.0.0.1", &ip4addr.sin_addr);

s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip4addr, sizeof ip4addr);
// IPv6:

struct sockaddr_in6 ip6addr;
int s;

ip6addr.sin6_family = AF_INET6;
ip6addr.sin6_port = htons(4950);
inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &ip6addr.sin6_addr);

s = socket(PF_INET6, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip6addr, sizeof ip6addr);
mengmingtao 2011-11-02
  • 打赏
  • 举报
回复
明白了。
[Quote=引用 4 楼 qq120848369 的回复:]

引用 3 楼 qq120848369 的回复:

引用 2 楼 mengmingtao 的回复:

但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个
引用 1 楼 qq120848369 的回复:

struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所……
[/Quote]
qq120848369 2011-11-02
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 qq120848369 的回复:]

引用 2 楼 mengmingtao 的回复:

但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个
引用 1 楼 qq120848369 的回复:

struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_fa……
[/Quote]

p==q,因为p和q都是指针变量,存储一个4字节的地址而已,我可以把那个地址开始的内存当做4字节的,也可以当做8字节的double,当然这个例子不太合适,因为如果当做8字节的double会越界int a。

这一段和例子说的不对,改成这样:

int a;
int *p=&a;
double *q=(double*)&a;

然后理解那一段话,a所在的内存本来只有4字节合法,当做double会越界4字节,当然Int刚合适。

对于sockaddr,不会有越界问题,因为是传进去地址,根据family决定那个地址里存得到底是什么结构体。
qq120848369 2011-11-02
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 mengmingtao 的回复:]

但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个
引用 1 楼 qq120848369 的回复:

struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_family_t sa_family; 的,它的作用就是提……
[/Quote]

指针是一个4字节的变量,用来存储地址,至于存的这个地址到底指向什么都无所谓,等进了函数里再根据family做转型就可以了。

举个例子:
int a;
int************************ *p=&a;
double********************************************* *q=(double********************************************* *)&a;

p==q,因为p和q都是指针变量,存储一个4字节的地址而已,我可以把那个地址开始的内存当做4字节的int,也可以当做8字节的double,当然这个例子不太合适,因为如果当做8字节的double会越界int a。

当然sockaddr没有这个问题,我们只是想把结构体的地址传进去,然后让函数检查family字段,从而转型为不同的结构体进行进一步处理。
mengmingtao 2011-11-02
  • 打赏
  • 举报
回复
但是对于struct sockaddr_in6来说,它要占28个byte,而struct sockaddr才占16个
[Quote=引用 1 楼 qq120848369 的回复:]

struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_family_t sa_family; 的,它的作用就是提前占位足够的内存,并且sockaddr和sockaddr_in之间可以共同访问第一个字段family。

内存是没有类型的,怎么解释内存完全是指针类型的关系。

所以……
[/Quote]
qq120848369 2011-11-02
  • 打赏
  • 举报
回复
struct sockaddr , 你看到 char sa_data[14];了吧,char可以在任意字节上对齐,所以 char sa_data[14];是紧邻sa_family_t sa_family; 的,它的作用就是提前占位足够的内存,并且sockaddr和sockaddr_in之间可以共同访问第一个字段family。

内存是没有类型的,怎么解释内存完全是指针类型的关系。

所以为了统一使用方法,只要用户强制把sockaddr_in6/sockadd_in的内存解释为sockaddr类型就可以了。

我估计
unsigned short int sin6_family; sa_family_t sa_family 是一样的类型。

23,121

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧