socket
在一个典型的客户端/服务器场景中,应用程序使用 socket 进行通信的方式如下:
-
各个应用程序创建一个 socket,socket 是一个允许通信的 "设备",两个应用程序都要用到它
-
服务器将自己的 socket 绑定到一个众所周知的地址上使得客户端能够定位到它的位置
套接字地址格式
在使用套接字时,首先要解决通信双方寻址的问题。我们需要套接字的地址建立连接。
通用套接字地址格式
sockaddr
是一个通用的地址结构,通用的意思是适用于多种地址族。这个类型的唯一用途是将各种特定的地址结构转换成单个类型以供 socket 系统调用使用。
/* POSIX.1g 规范规定了地址族为2字节的值. */
typedef unsigned short int sa_family_t;
/* 描述通用套接字地址 */
struct sockaddr{
sa_family_t sa_family; /* 地址族. 16-bit*/
char sa_data[14]; /* 具体的地址值 112-bit */
};
sa_family
是地址族,它表示使用什么样的方式对地址进行解释和保存:
-
AF_LOCAL
:表示的是本地地址,对应的是 Unix 套接字,这种情况一般用于本地 socket 通信,很多情况下也可以写成AF_UNIX
、AF_FILE
-
AF_INET
:因特网使用的 IPv4 地址 -
AF_INET6
:因特网使用的 IPv6 地址
AF_
表示的含义是 Address Family
,但是很多情况下,我们也会看到以 PF_
表示的宏,意思是 Protocol Family
,也就是协议族的意思。<sys/socket.h>
头文件中可以清晰地看到,这两个值本身就是一一对应的:
/* 各种地址族的宏定义 */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
IPv4 套接字格式地址
/* IPV4套接字地址,32bit值. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
/* 描述IPV4的套接字地址格式 */
struct sockaddr_in
{
sa_family_t sin_family; /* 16-bit */
in_port_t sin_port; /* 端口口 16-bit*/
struct in_addr sin_addr; /* Internet address. 32-bit */
/* 这里仅仅用作占位符,不做实际用处 */
unsigned char sin_zero[8];
};
IPv4 地址是一个32-bit的字段,可以想象最多支持的地址数就是 2 的 32 次方,大约是 42 亿。
端口号最多是16-bit,也就是说最大支持 2 的 16 次方,所以我们应该知道支持寻址的端口号最多就是65535。
IPv6 套接字地址格式
struct sockaddr_in6
{
sa_family_t sin6_family; /* 16-bit */
in_port_t sin6_port; /* 传输端口号 # 16-bit */
uint32_t sin6_flowinfo; /* IPv6流控信息 32-bit*/
struct in6_addr sin6_addr; /* IPv6地址128-bit */
uint32_t sin6_scope_id; /* IPv6域ID 32-bit */
};
本地套接字格式
以上无论 IPv4 还是 IPv6 的地址格式都是因特网套接字的格式,还有一种本地套接字格式,也就是前面提到的 AF_LOCAL
。
struct sockaddr_un {
unsigned short sun_family; /* 固定为 AF_LOCAL */
char sun_path[108]; /* 路径名 */
};
几种套接字地址格式比较
IPv4 和 IPv6 套接字地址结构的长度是固定的,而本地地址结构的长度是可变的: