前言
读过《UNIX网络编程》的同学都知道,这本书详细介绍了套接字的方方面面,并且给出了每一个结构体、
系统API的实现代码文件。不过技术不断发展,操作系统的内核也不断更新,书中描述相关地址结构体,和
具体实现源文件有时候对不上。
鉴于此,本文基于书上描述,结合linux操作系统进行相关笔记记录,便于阅读的时候,方便查找。
依赖:
- 书籍《UNIX网络编程》(卷一) 版本3
- 操作系统平台:Centos8 【Linux Centos 3.10.0-1160.49.1.el7.x86_64】
如果无特别说明,本文章内容都基于这操作系统平台。
笔记:
IPv4套接字地址结构:
sokcaddr_in
,定义文件:/usr/include/netinet/in.h
29 /* Internet address. */
30 typedef uint32_t in_addr_t;
31 struct in_addr
32 {
33 in_addr_t s_addr;
34 };
237 /* Structure describing an Internet socket address. */
238 struct sockaddr_in
239 {
240 __SOCKADDR_COMMON (sin_);
241 in_port_t sin_port; /* Port number. */
242 struct in_addr sin_addr; /* Internet address. */
243
244 /* Pad to size of `struct sockaddr'. */
245 unsigned char sin_zero[sizeof (struct sockaddr)
246 - __SOCKADDR_COMMON_SIZE
247 - sizeof (in_port_t)
248 - sizeof (struct in_addr)];
249 };
__SOCKADDR_COMMON
和__SOCKADDR_COMMON_SIZE
是一个宏定义,
其定义文件:/usr/include/bits/sockaddr.h
。
34 #define __SOCKADDR_COMMON(sa_prefix) \
35 sa_family_t sa_prefix##family
36
37 #define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
因此实际上sokcaddr_in
结构体定义如下:
struct sokcaddr_in
{
sa_family_t sin_family;
int_port_t sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[sizeof (struct sockaddr)
- (sizeof (unsigned short int))
- sizeof (in_port_t)
- sizeof (struct in_addr)];
};
sa_family_t
定义在:/usr/include/bits/sockaddr.h
:
27 /* POSIX.1g specifies this type name for the `sa_family' member. */
28 typedef unsigned short int sa_family_t;
无符号的短整数,长度16位。
in_port_t
定义在:/usr/include/netinet/in.h
。
118 /* Type to represent a port. */
119 typedef uint16_t in_port_t;
无符号16位的整型,表示端口号。端口号在IPv4
下,总是以网络字节序来保存。
通用套接字地址结构:
通用套接字的出现是为了满足套接字函数能处理来自所支持的任何协议族的套接字结构地址。对于C语言
来说直接void*
指针即可,但是套接字出现的时候,ANSI C还没出现(套接字最初出现在1982年),
因此设计人员定义了一个通用的地址结构struct sockaddr
。当前该结构体定义在:
/usr/include/bits/socket.h
。
177 /* Structure describing a generic socket address. */
178 struct sockaddr
179 {
180 __SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
181 char sa_data[14]; /* Address data. */
182 };
展开宏__SOCKADDR_COMMON
定义,最后struct sockaddr
定义如下:
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
}
于是套接字被定义为以指向某个通用套接字地址结构的指针作为其参数之一。这就要求对于这些函数的调用
需要将指定特定协议的地址结构体指针进行强制类型转换,变成指向某个通用套接字地址结构的指针,以适应
函数参数要求。
IPv6套接字地址结构
IPv6
协议套接字地址结构体是:struct sockaddr_in6
,
定义在:/usr/include/netinet/in.h
。
211 /* IPv6 address */
212 struct in6_addr
213 {
214 union
215 {
216 uint8_t __u6_addr8[16];
217 uint16_t __u6_addr16[8];
218 uint32_t __u6_addr32[4];
219 } __in6_u;
220 #define s6_addr __in6_u.__u6_addr8
221 #ifdef __USE_MISC
222 # define s6_addr16 __in6_u.__u6_addr16
223 # define s6_addr32 __in6_u.__u6_addr32
224 #endif
225 };
253 struct sockaddr_in6
254 {
255 __SOCKADDR_COMMON (sin6_);
256 in_port_t sin6_port; /* Transport layer port # */
257 uint32_t sin6_flowinfo; /* IPv6 flow information */
258 struct in6_addr sin6_addr; /* IPv6 address */
259 uint32_t sin6_scope_id; /* IPv6 scope-id */
260 };
宏展开后实际定义如下:
struct sockaddr_in6
{
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 sockaddr_storage
结构体克服了老的struct sockaddr
通用套接字结构体的缺点。
新的结构体足以容纳系统所支持的任何套接字地址结构。
其定义在头文件:/usr/include/bits/socket.h
。
185 /* Structure large enough to hold any socket address (with the historical
186 exception of AF_UNIX). */
187 #define __ss_aligntype unsigned long int
188 #define _SS_PADSIZE \
189 (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype))
190
191 struct sockaddr_storage
192 {
193 __SOCKADDR_COMMON (ss_); /* Address family, etc. */
194 char __ss_padding[_SS_PADSIZE];
195 __ss_aligntype __ss_align; /* Force desired alignment. */
196 };
__SS_SIZE
和__SOCKADDR_COMMON_SIZE
。都定义在:/usr/include/bits/sockaddr.h
#define __SS_SIZE 128
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
因此,宏展开后该结构体定义如下:
struct sockaddr_storage
{
sa_family ss_family;
char __ss_padding[128-sizeof(unsigned short int)-sizeof(unsigned long int)];
unsigned long int __ss_align;
}
【摘】sockaddr_storage
类型提供的通用套接字地址结构相比sockaddr
存在以下两点差别。
- 1、如果系统支持的任何套接字地址结构有对齐需要,那么sockaddr_storage能够满足最苛刻的对齐要求。
- 2、sockaddr_storage最够大,能够容纳系统支持的任何套接字地址结构。
注意:除了ss_family其他字段对用户来说是透明的。sockage_storage结构必须类型强制转换成或者复制
适合于ss_family字段所给出地址类型的套接字地址结构中,才能访问其他字段。