1. 套接口地址结构
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 */
struct in_addr sin_addr; /* 32-bit IPv4 address */
/* network byte ordered */
char sin_zero[8]; /* unused */
};
sin_len
长度成员,是为了增加OSI协议的支持,Posix.1g 不要求有这个成员。
sin_family
协议族标识,在支持长度成员的实现用,一般是8位无符号整数;在不支持长度成员的实现中,一般是16位无符号整数。
sin_port
端口号标识,必须是至少16位的无符号整数。
sin_addr
地址标识,必须是至少32位无符号整数。
sin_zero
暂时不使用。
通用套接口地址结构:
由于作为参数传递给套接口函数时,套接口地址结构总是通过指针来传递。所以为了保证套接口函数支持任意协议族的套接口地址结构, 在<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 */
};
于是套接口函数被定义为采用指向通用套接口地址结构的指针,例如:
int bind ( int, struct sockaddr* , socklen_t );
IPv6套接口地址结构:
IPv6套接口地址结构定义在头文件<netinet/in.h>
struct in6_addr
{
uint8_t s6_addr[16]; /* 128-bit IPv6 address */
/* network byte ordered */
};
struct sockaddr_in6
{
uint8_t sin6_len; /* length of this struct(24) */
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port# */
/* network byte ordered */
uint32_t sin6_flowinfo; /* priority & flow label */
/* network byte ordered */
struct in6_addr sin6_addr; /* IPv6 address */
/* network byte ordered */
};
2. 字节序及字节排序函数
内存中存储两个字节有两种方式:一种是将低序字节存储在起始地址,称为小端(little-endian)字节序;另一种方法是将高序字节存储在起始地址,称为大端(big-endian)字节序。
不同操作系统采用的主机字节序是不同的。网络协议必须指定一个网络字节序(network byte order),网际协议在处理这些多字节整数时,使用大端字节序。而Posix.1g规定,套接口地址结构中的某些成员按网络字节序维护。这样我们必须考虑主机字节序和网络字节序之间的相互转换了。
常用的转换函数如下:
#include <netinet/in.h>
// host to network
uint16_t htons(uint16_t host16bitvalue );
uint32_t htonl(uint32_t host32bitvalue );
// network to host
uint16_t ntohs(uint16_t net16bitvalue );
uint32_t ntohl(uint32_t net32bitvalue );