套接字地址 每个协议族都定义它自己的套接字地址结构。这些结构的名字均以 sockaddr_ 开头,并以对应每个协议族的唯一后缀结尾。
IPv4套接字地址结构(网际套接字地址结构)
<netinet/in.h>
struct in_addr{
in_addr_t s_addr; /*32-bit IPv4 address*/
/*network byte ordered【网络字节序】*/
}
struct sockaddr_in{
uint8_t sin_len; /*length of structure (16)除路由套接字,其他无需设置和检查*/
sa_family_t sin_family; /*AF_INET*/
in_port_t sin_port; /*16-bit TCP or UDP port number*/
/*network byte ordered*/
strcuct in_addr sin_addr; /*32-bit IPv4 address*/
/*network byte ordered*/
char sin_zero[8]; /*unused*/
}
- IPv4地址和TCP/UDP端口号在套接字地址结构中总是以网络字节序来存储
- 32位IPv4地址存在两种 不同的访问方法:例如,serv定义为某个网际套接字地址结构,那么serv.sin_addr将按in_addr结构引用其中的32位IPv4地址,而serv.sin_addr.s_addr将按in_addr_t(通常是一个无符号32位整数)引用同一个32位IPv4地址
- 套接字地址结构仅在给定主机上使用:虽然结构中的某些字段(IP地址和端口号)用在不同主机之间的通信中,但是结构本身并不在主机之间传递
通用套接字
<sys/socket.h>
struct sockaddr{
uint8_t sa_len;
sa_family_t sa_family; /*address family:AF_xxx value*/
char sa_data[14]; /*protocol_specific address*/
};
IPv6套接字
<netinet/in.h>
struct in6_addr{
unit8_t s6_addr[16]; /*128-bit IPv6 address*/
/*network byte ordered*/
};
#define SIN6_LEN /*required for compile_time tests*/
struct sockaddr_in6{
unit8_t sin6_len; /*length of this struct(28)*/
sa_family_t sin6_family; /*AF_INET6*/
in_port_t sin6_port; /*transport layer port*/
/*network byte ordered*/
uint32_t sin6_flowinfo; /*flow information , undefined*/
struct in6_addr sin6_addr; /*IPv6 address*/
/*network byte ordered*/
uint32_t sin6_scope_id; /*set of interfaces for a scope*/
}
3.1 值—结果参数
当往一个套接字函数传递一个套接字地址结构时,该结构总是以引用形式来传递,也就是说传递的是指向该结构的一个指针。该结构的长度也作为一个参数来传递,不过其传递方式取决于该结构的传递方向:是从进程到内核,还是从内核到进程。
(1)从进程到内核传递套接字地址结构的函数有3个:bind、connect和sendto。
(2)从内核到进程传递套接字地址结构的函数有4个:accept、recvfrom、getsockname和getpeername。(值—结果参数)
把套接字地址结构大小这个参数从一个整数改为指向某个整数变量的指针,其原因在于:当函数被调用时,结构大小是一个值,它告诉内核结构的大小,这样内核在写该结构时不至于越界;当函数返回时,结构大小又是一个结果,它告诉进程内核在该结构中究竟存储了多少信息。
3.2 字节排序函数
考虑一个16位整数,它由2个字节组成。内存中存储这两个字节有两种方法:一种是将低序字节存储在起始地址,这称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,这称为大端(big-endian)字节序。
- MSB most significant bit 最高有效位 16位值最左边一位
- LSB least significant bit 最低有效位 16位值最右边一位
- 网际协议使用大端字节序来传递这些多字节整数
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue); 均返回:网络字节序的值
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue); 均返回:主机字节序的值
h代表host,n代表network,s代表short,l代表long
S视为一个16位的值(例如TCP或UDP端口号),l视为一个32位的值(例如IPv4地址)。
3.3 字节操作函数
Berkeley函数:
#include <string.h>
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptrl,const void *ptr2,size_t nbytes);
//返回:若相等则为0,否则为非0
ANSI C函数:
#include <string.h>
void *memset(void *dest,int c,size_t len);
void *memcpy(void *dest,const void *src,size_t nbytes);
int memcmp(const void *ptrl, const void *ptr2, size_t nbytes);
//返回:若想等则为0,否则为<0或>0
3.4 地址转换函数
1.点分十进制数串(例如“206.168.112.96”)与它长度为32位的网络字节序二进制值间转换IPv4地址
#include <arpa/inet.h>
/*将strptr所指C字符串转换成一个32位的网络字节序二进制值,并通过指针addrptr来返回*/
int inet_aton(const char *strptr, struct in_addr *addrptr);
//返回:若字符串有效则为1,否则为0
/*已经被废弃*/
int_addr_t inet_addr(const char *strptr);
//返回:若字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE
/*将一个32位的网络字节序二进制IPv4地址转换成相应的点分十进制数串。
由该函数的返回值所指向的字符串驻留在静态内存中。*/
char *inet_ntoa(struct in_addr inaddr);
//返回:指向一个点分十进制数串的指针
inet_pton
和inet_ntop
函数对于IPV4地址和IPv6地址都适用
函数名中p和n分别代表表达(presentation)和数值(numeric),地址的表达式通常是ASCII字符串,数值格式则是存放到套接字地址结构中的二进制值。
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);
//返回:若成功则为1,如输入不是有效的表达格式则为0,若出错则为-1
注:addrptr是网络字节序
const char *inet_ntop(int family, const void *addrptr, char *strptr,size_t len);
//返回:若成功则为指向结果的指针,若出错则为NULL
- 这两个函数的family参数既可以是AF_INET,也可以是AF_INET6。如果以不被支持的地址族作为family参数,这两个函数就都返回一个错误,并将errno置为EAFNOSUPPORT。
- 第一个函数转换由strptr指针所指的字符串,并通过addrptr指针存放二进制结果。
- 第二个函数从数值格式(addrptr)转换到表达格式(strptr)。