之前一直认为是sockaddr设计时有缺陷,在编写网络通信时,都使用sockaddr_in,因为它将sockaddr中的
char sa_data[14]
拆分为了
unsigned short sin_port; // 2 bytes
struct in_addr sin_addr; // 4 bytes
char sin_zero[8]; // 8 bytes zero
这样就可以直观的获得IP地址和端口。
最近在获取网卡等信息的时候,使用ioctl()函数,发现需要一个数据结构是struct ifreq(如下ioctl()获取MAC地址)
ioctl(fd, SIOCGIFHWADDR, &(struct ifreq)ifr)
struct ifreq定义在if.h中,它有许多sockaddr结构的变量
struct ifreq {
#define IFHWADDRLEN 6
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
void * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
就如同我们刚刚获得的MAC地址,我们通过ifr.ifr_hwaddr来获取它,而它的类型就是sockaddr!(ifr_hwaddr宏定义如下,也就是对应struct ifreq中的ifr_ifru.ifru_hwaddr)
#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
也就是说,sockaddr_in可以视为sockaddr对于TCP/IP协议的拓展衍生,sockaddr可以存储的类型非常之多。
准确说应该是对于TCP/UDP的拓展,因为他们两个协议才定义了端口(Port)这个东西。(所以sockaddr_in.sin_family通常是AF_INET和AF_INET6两种类型)
我们平常几乎都是针对TCP/IP编程,所以对于编程者而言,几乎也就都使用sockaddr_in;而对于系统而言,它还是更喜欢使用sockaddr。比如我们在使用bind(),accept()等函数的时候就需要将sockaddr_in强制转换成sockaddr。