1.网络编程中的地址结构
在网络编程中,我们经常会进行地址类型的转换,多是因为我们能够是别的地址为文本表示,而计算机网络能识别的为socket结构的二进制值表示。我们现总结一下地址的两个传递方向:
(1)从进程传递到内核from the process to the kernel;(2)从内核传递到进程from the kernel to the process.
地址转换函数:(1)大部分IPv4结构的地址采用函数inet_addr and inet_ntoa;(2)但是有俩个新的函数 inet_pton and inet_ntop既能转换IPv4也能转换IPv6结构的地址。
存在的问题是:上面的地址转换函数依赖于所转换的地址机构是IPv4还是IPv6?----如何解决?如何使其变成protocol-independent的函数?
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 */
};
通用地址结构 <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 */
};
这些数据结构的一般用法:
step1:首先,定义一个sockaddr_in 的结构实例,并将它清0;
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr));或者bzero(&serv_addr,sizeof(struct sockaddr));
step2:为这个结构体赋值
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(8080);
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
step3:在函数调用中使用这个结构时,将其强制转换为sockaddr类型。
socklen_t addr_len;
addr_len=sizeof(struct sockaddr);
bind(listen_fd,(struct sockaddr *)&serv_addr,&addr_len);
accept(listen_fd,(struct sockaddr *)&client_addr,&addr_len);
2."value-result":可以双向传递的参数叫做value-reault argument.准确的说,在函数调用中,传递值给被调用函数,又获取被调用函数对该参数修改后结果的参数。可以参考下面的描述。
the size is both a value when the function is called (it tells the kernel the size of the structure so that the kernel does not write past the end of the structure when filling it in) and a result when the function returns (it tells the process how much information the kernel actually stored in the structure). This type of argument is called a value-result argument.
3.网络编程中的字节序问题
事实上,不同的硬件平台会有不同的字节序,但是在网络中必须按照网络字节序(即大端模式字节序)。
不同的CPU的主机中,内存存储multi-bytes整数序列有两种方法,称为主机字节序(HBO---host byte order):
小端字节序(little-endian)即,低字节存储在低地址,将低字节存储在起始地址,称为"little-endian"字节序,Intel AMD 等采用这种模式的字节序;
大端字节序(big-endian)即,高字节存储在低地址,将高字节存储在起始地址,称为"big-endian"字节序,由Macintosh、motorola等所采用。
所以在网络通信中必须考虑字节序的问题,最好的方案应该是,在传输前将其转换为网络字节序(NBO Network byte order大端模式字节序),接受后先将其转换为主机字节序在进行相关的处理。
字节序的转换函数: #include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); 32位主机字节序转换为网络字节序
uint16_t htons(uint16_t hostshort); 16位主机字节序转换为网络字节序
uint32_t ntohl(uint32_t netlong); 32位网络字节序转换为主机字节序
uint16_t ntohs(uint16_t netshort); 16位网络字节序转换为主机字节序
DESCRIPTION
The htonl() function converts the unsigned integer hostlong from host
byte order to network byte order. 函数htonl转换无符号无符号整型(unsigned
int)主机数据为网络字节序
The htons() function converts the unsigned short integer hostshort from
host byte order to network byte order. 函数htons转换无符号短整型(unsigned
short int)主机数据为网络字节序
The ntohl() function converts the unsigned integer netlong from network
byte order to host byte order.函数ntohl转换无符号整型(unsigned int)
网络字节序数据为主机字节序
The ntohs() function converts the unsigned short integer netshort from
network byte order to host byte order. 函数ntohs 转换无符号短整型网络字节
序数据为主机字节序
On the i386 the host byte order is Least Significant Byte first,
whereas the network byte order, as used on the Internet, is Most Sig-
nificant Byte first. i386主机字节序是最小有效字节在起始地位(小端模式),然而网络字节
序(用在网络中的字节序)是最大有效字节在其实地址(大端模式)
4.IPv4地址转换函数:inet_aton, inet_ntoa, and inet_addr #include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr); Returns: 1 if string was valid, 0 on error
这个函数转换strptr指向的字符串为32位二进制网络字节序值,存储为in_addr类型可以直接赋值给in_addr结构。
一个未说明的特点是:如果addrptr为NULL,该函数仍然认为输入字符串有效,但不存储任何结果。
in_addr_t inet_addr(const char *strptr);Returns: 32-bit binary network byte ordered IPv4 address; INADDR_NONE if error
该函数功能同上,只是该函数一返回值的形式来返回转换为32位二进制网络字节序的值。
该函数存在的问题是:
The problem with this function is that all 232 possible binary values are valid IP addresses (0.0.0.0 through 255.255.255.255), but the function returns the constant INADDR_NONE (typically 32 one-bits) on an error.
This means the dotted-decimal string 255.255.255.255 (the IPv4 limited broadcast address, Section 20.2) cannot be handled by this function since its binary value appears to indicate failure of the function.
A potential problem (潜在问题)with inet_addr is that some man pages state that it returns 1 on an error, instead of INADDR_NONE.
This can lead to problems, depending on the C compiler, when comparing the return value of the function (an unsigned value) to a negative constant.
Today, inet_addr is deprecated (放弃使用,不赞成使用)and any new code should use inet_aton instead.
Better still is to use the newer functions described in the next section, which handle both IPv4 and IPv6.
char *inet_ntoa(struct in_addr inaddr);Returns: pointer to dotted-decimal string
该函数转换一个32位二进制网络字节序的值(IPv4)为对应的点分十进制字符串,值得注意的是,该函数以一个结构体作为其型参,实际上这种用法是比较少见的。该函数不可重入。
新的IPv4和IPv6均可使用的地址转换函数:#include <arpa/inet.h>
The letters "p" and "n" stand for presentation and numeric.
The presentation format for an address is often an ASCII string and the numeric format is the binary value that goes into a socket address structure.
下面两个函数的family参数值为AF_INET或者AF_INET6,如果family参数错误,或者不支持,则两个函数错误返回,错误代码为:EAFNOSUPPORT。
int inet_pton(int family, const char *strptr, void *addrptr);Returns: 1 if OK, 0 if input not a valid presentation format, -1 on error
该函数转换strptr指向的字符串为32位二进制网络字节序的值,并存储在addrptr指向的地址,成功返回1,错误返回0。
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);Returns: pointer to result if OK, NULL on error
该函数转换一个32位二进制网络字节序的值(IPv4)为对应的点分十进制字符串,len指示strptr指向空间的最大长度,防止该函数些的内容超出指定的范围,造成内存溢出或泄露,为了这个目的,还有俩个常量被定义在<netinet/in.h>中,如下:
#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */
如果len太小,无法保存转换结果的字符串转换值,一个空指针NULL返回,并返回错误代码:ENOSPC。