由于网络协议族的不同、大小端的不同、人与机器对数据的识别不同,所以要对一些数据结构进行统一和转换。
1.对于网络协议族:有IPV4,IPV6,UNIX,DATALINK等的不用,导致他们对应的套接字地址结构也不同。
ipv4的套接字地址结构
struct sockaddr_in {
unit8_t sin_len;
sa_family_t sin_family;
int_prot_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中
struct in_addr {
in_addr_t s_addr;
};
ipv6的套接字地址结构
struct sockaddr_in6 {
uint8_t sin6_len;
sa_family_t sin6_family;
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
};
其中
struct in6_addr {
unit8_t s6_addr[16];
};
从这两个结构中我们可以找出一些结构变量的共同点:①有标明是何种协议族的成员变量sa_family,②有标明地址的成员变量 ③有标明端口的变量 ④有标明地址结构长度的变量。
对于这种情况,为了能够用共同的函数对不同的套接字地址结构作统一的处理,就引进了一个结构体:通用套接字地址结构
struct sockaddr {
unit8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
其实对函数传入不同套接字地址结构指针完全可以用void *可以实现,但是由于历史原因,该结构早于void *的出现,所以沿用下来。
2.对大小端的统一。这种情况出现是由于:网络字节序均为大端字节序,而不同的主机系统的字节序可能会不同,可能是大端也可能是小端。于是为了实现对主机与网络之间数据的正确传送,就需要大小端转换了。
用如下四个函数
#include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(unit32_t host32bitvalue);
unit16 ntohs(uint16_t net16bitvalue);
unit32 ntohl(unit32_t net32bitvalue);
为了便于记忆 h 为host n为network,s 为short,l 为long ,注意不可将s简单的理解为short型,long理解为long型,因为64位的系统long型为64位,但是这些函数只能处理16位或者32位的数,64位的是无法处理的。
3.对网络地址,机器与人的识别的统一转换
计算机识别网络地址是用二进制的,而为了便于理解,人为的网络地址是字符串的。所以要实现这两者的转换,于是又下面几个函数。
inet_aton,inet_addr,inet_ntoa,inet_pton,inet_ntop
注意:前三个只能处理IPV4,后两个既可以处理IPV4也可以处理IPV6,而且后两个更为常用
为了便于记忆:可将a理解为ASCII,n理解为numberic,p理解为presentation(表达式)
int inet_aton(const char *strptr,struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
char *inet_ntoa(struct in_addr inaddr);
int inet_pton(int family,const char *strptr,void *addrptr);
const char *inet_ntop(int family,const void *addrptr,char *strptr,size_t len);
为了便于记忆总结下着两组函数的共同点:
①如果函数的参数中 源和目标同时存在即既有str 又有addr的话,被转换的总在转换成的前面。
②如果最终是转换成str的话,其返回的一个是个指向char的指针。
还有注意点:
①后一组将为通用的函数有了family这个参数的原因是 其皆可以处理IPV4又可以处理IPV6
②最后一个之所以加了个len的参数,是给定返回字符串的长度,防止返回溢出调用者的缓冲区。
参考文献:《unix网络编程第一卷》
《unix环境高级编程》第二版