linux connect ip 历史,Linux中常见的ip地址以及socket 地址表示方法

最近与IP地址以及socket 地址打交道比较多, 碰到几个常见的套接字:

sockaddr_in - ipv4 地址, number表示法 4个字节即可, 比如字面量表示法是"255.255.255.255", 转成数字表示法, ff.ff.ff.ff => ffffffff

sock_addr_in6 -- ipv6地址, number表示法 16个字节即可

sockaddr -- 常见socket API的 socket 地址通用表示法, 16个字节

sockaddr_storage -- 为兼容ipv6的socket地址通用表示法, 28 个字节

IPv4 address以及 Socket address for IPv4 , in_addr 是4个字节, IPv4 的Socket address是16个字节

typedef __uint8_t sa_family_t;

typedef __uint16_t in_port_t;

typedef __uint32_t in_addr_t; /* base type for internet address */

/*

* Internet address (a structure for historical reasons)

* Ipv4 的地址, 历史原因

*/

struct in_addr {

in_addr_t s_addr;

};

/*

* Socket address, internet style.

*/

struct sockaddr_in {

__uint8_t sin_len;

sa_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

IPv6 address以及 Socket address for IPv6 , IPv6 的Socket address 是28个字节

typedef __uint8_t sa_family_t;

typedef __uint16_t in_port_t;

/*

* IPv6 address 16个字节 128bit

*/

typedef struct in6_addr {

union {

__uint8_t __u6_addr8[16];

__uint16_t __u6_addr16[8];

__uint32_t __u6_addr32[4];

} __u6_addr; /* 128-bit IP6 address */

} in6_addr_t;

#define s6_addr __u6_addr.__u6_addr8

#define INET6_ADDRSTRLEN 46

/*

* Socket address for IPv6

*/

struct sockaddr_in6 {

__uint8_t sin6_len; /* length of this struct(sa_family_t) */

sa_family_t sin6_family; /* AF_INET6 (sa_family_t) */

in_port_t sin6_port; /* Transport layer port # (in_port_t) */

__uint32_t sin6_flowinfo; /* IP6 flow information */

struct in6_addr sin6_addr; /* IP6 address */

__uint32_t sin6_scope_id; /* scope zone index */

};

为了统一各种协议, socket对应的接口就定义了两个通用结构, 分别是sockaddr(16字节)和sockaddr_storage(128字节),两个通用套接字的结构体, 其中sockaddr_storage是为了适配sockaddr_in6(128字节)这样长度比较大的协议而后来定义的,如果需要用到sockaddr_storage这样的通用套接字,则强转为sockaddr,并且长度用sizeof(struct sockaddr_storage)

下图中, struct sockaddr 是16个字节, struct sockaddr_storage是28个字节

/*

* [XSI] Structure used by kernel to store most addresses.

*/

struct sockaddr {

__uint8_t sa_len; /* total length */

sa_family_t sa_family; /* [XSI] address family */

char sa_data[14]; /* [XSI] addr value (actually larger) */

};

/*

* RFC 2553: protocol-independent placeholder for socket addresses

*/

#define _SS_MAXSIZE 128

#define _SS_ALIGNSIZE (sizeof(__int64_t))

#define _SS_PAD1SIZE \

(_SS_ALIGNSIZE - sizeof(__uint8_t) - sizeof(sa_family_t))

#define _SS_PAD2SIZE \

(_SS_MAXSIZE - sizeof(__uint8_t) - sizeof(sa_family_t) - \

_SS_PAD1SIZE - _SS_ALIGNSIZE)

/*

* [XSI] sockaddr_storage

*/

struct sockaddr_storage {

__uint8_t ss_len; /* address length */

sa_family_t ss_family; /* [XSI] address family */

char __ss_pad1[_SS_PAD1SIZE];

__int64_t __ss_align; /* force structure storage alignment */

char __ss_pad2[_SS_PAD2SIZE];

};

在Linux 网络编程中, 不管用的是什么协议, 使用的都是同一套socket API, 而每个协议表示网络地址的结构体是不一样的, 如IPv4是struct sockaddr_in, IPv6是struct sockaddr_in6, 所以需要将所有这些表示网络地址的结构体抽象成一个通用的结构体, 抽象之后的结构体便是struct sockaddr.

常见的涉及通用socket结构体的API为:

/*application->kernel*/

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

int connect(int sockfd, const struct sockaddr *serv_addr,

socklen_t addrlen);

int sendto (int s, const void *msg, size_t len, int flags,

const struct sockaddr *to, socklen_t tolen);

/*kernel->application*/

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

int recvfrom (int s, void *buf, size_t len, int flags,

struct sockaddr *from, socklen_t *fromlen);

int getpeername(int s, struct sockaddr *name, socklen_t *namelen);

int getsockname(int s, struct sockaddr *name, socklen_t *namelen);

那为什么会出现struct sockaddr_storage这个通用结构体呢?

身为一个通用的地址数据结构, 它的大小得要是所有具体协议地址结构大小的最大值, 可是sizeof(struct sockaddr) = 16, 而sizeof(struct sockaddr_in6) = 28, 显然struct sockaddr这个通用的数据结构hold不住IPv6啊,所以struct sockaddr_storage这个新结构横空出世了,它的大小为128字节,应该能装得下目前所以协议的地址结构了。

在涉及多种协议的网络编程中,用struct sockaddr_storage这个结构体来表示通信地址,调用socket API时,需要将struct sockaddr_storage强制类型转换为struct sockaddr,地址长度为sizeof(struct sockaddr_storage)。如:

struct sockaddr_storage addr;

/*

地址赋值

*/

sendto (s, *msg, len, flags, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));

一个IPv4/IPv6混合编程的Demo:

struct sockaddr_storage addr;

memset(&addr, 0, sizeof(struct sockaddr_storage));

if (isIPv6 == TRUE)

{

struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)&addr;

addr_v6->sin6_family = AF_INET6;

addr_v6->sin6_port = 1234;

inet_pton(AF_INET6, “2001:3211::1”, &(addr_v6->sin6_addr));

}

else

{

struct sockaddr_in *addr_v4 = (struct sockaddr_in *)&addr;

addr_v4->sin_family = AF_INET;

addr_v4->sin_port = 1234;

inet_aton(“192.168.1.228”, &(addr_v4->sin_addr));

}

sendto(sock, buf, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_storage));

参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值