套接字地址结构
每个协议族都定义自己的套接字地址结构,这些结构的名字均以sockaddr_开头,并以对应每个协议族的后缀结尾.
IPv4套接字地址结构(网际套接字地址结构)
头文件:<netinet/in.h>
POSIX定义:
struct in_addr{
in_addr_t s_addr; // 至少一个32位无符号整形,uint32_t
};
// 所有套接字地址结构大小都至少16字节
struct sockaddr_in{
uint8_t sin_len; // 这个不是必须的,我们无须设置和检查它.这个字段简化了长度可变的地址结构的处理(最少16 字 节)
sa_family sin_family; // 任何无符号整数类型(支持长度实现中8位,不支持长度实现中16位) AF_INET
in_port_t sin_port; // 至少16位的无符号整形,网络字节序来表示
struct in_addr sin_addr; // 至少32位
char sin_zero[8]; // 8字节,未曾使用,在填写地址结构时将其设置为0.
};
结构本身不在主机之间通信.
通用套接字地址结构
struct sockaddr{
uint8_t sa_len;
sa_family sa_family;
char sa_data[14];
}
从应用程序开发角度看,通用地址结果存在的意义在于将特定协议的套接字地址结构指针强制类型转换成通用地址结构
IPv6套接字地址结构
struct in6_addr{
unit8_t s6_addr[16];
};
#define SIN6_LEN // 如果支持sockaddr_in6长度字段,此常值必须定义.
struct sockaddr_in6{
uint8_t sin6_len;
sa_family_t sin6_family; // AF_INET6
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
新的通用地址结构
能够满足任何套接字地址结构
struct sockaddr_storage{
uint8_t ss_len;
sa_family_t ss_family;
}
当我们把指向套接字地址结构地址的指针传给一个套接字函数时,也把该结构的长度作为一个参数传递给函数
值-结果参数
套接字地址结构指针,套接字地址长度是值传递还是指针引用传递取决于传递的方向
1)从进程到内核:bind,connect,sendto connect(sockfd, (SA*)&serv, sizeof(serv));
2)从内核到进程:accept,recvfrom,getsockname,getpeername getpeername(unixfd, (SA*)&cli, &len);
len这个值告诉内核传递结构的大小,是内核在写结果时不至于越界.而在返回给进程时,这个值又被内核设置为实际写 入数据的大小.这种类型的参数称为值-结果参数.
其他一些值-结果参数:
1.select函数中间的3个参数;
2.getsockopt函数的长度参数;
3.使用recvmsg函数时,msghdr结构中的msg_namelen和msg_controllen字段;
4.ifconf结构中的ifc_len字段;
5.sysctl函数两个长度参数中的第一个.
字节排序函数
小端字节序:高地址(高字节序)--------->低地址(低字节序)
大端字节序:高地址(低字节序)<--------低地址(高字节序)
箭头表示数据读取方向
网络字节序:大端字节序
将主机字节序转换为网络字节序
#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表示net,s表示short,l表示long.我们应当把s视为一个16位的值,l视为一个32位的值.即使在64位系统中,这些函数处理long时仍然当作32位处理.使用这些函数使得程序可移植,而不必关心实际的字节序.另外,分组中数据字节序的问题也需要处理.位序是RFC文档中协议定义.
字节操纵函数
#include <string.h>
void bzero(void *dest, size_t nbytes);
void bcopy(const void *src, void *dest, size_t nbytes);
int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);
void *memset(void *dest, int c, size_t len);
void *memcpy(void *dest, const void *src, size_t nbytes); // 源字串与目标字串重叠时,操作结果不可知.bcopy能够正确处理.
int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);
inet_aton, inet_addr, inet_ntoa函数
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr); // 这个有一定问题,废弃.
char* inet_ntoa(struct in_addr addrptr); // 不可重入,传参是一个结构而不是指针
inet_pton和inet_ntop函数
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
readn,writen和readline函数
字节流套接字(例如TCP套接字)在调用read或write输入或输出的字节数可能比请求的少,然而这并不是出错的状态.源于内核套接字缓冲区可能已经达到了极限.此时需要再次调用read,write函数请求剩余的字节.这种现象在read时比较常见,在write时,只有此函数是非阻塞时才会出现.为了不让实现返回一个不足的字节计数值,我们总是改为调用writen代替write。