以下知识点来均来自steven先生所著UNP卷一(version3),刚开始学习网络编程,如有不正确之处请大家多多指正。
1、套接字地址结构
IPv4套接字地址结构:
struct in_addr { in_addr_t s_addr; //32位IPv4地址,网络字节序 }; struct sockaddr_in { uint8_t sin_len; //结构大小 sa_family_t sin_family;//对应的协议族AF_INET in_port_t sin_port; //16比特的端口 struct in_addr sin_addr; //32位IPv4地址,使用网络字节序 char sin_zero[8];//未使用部分 };
IPv6套接字地址结构:
struct in6_addr { uint8_t s6_addr[16]; //128位IPv6地址,网络字节序 }; #define SIN6_LEN struct sockaddr_in6 { uint8_t sin6_len; //结构大小 sa_family_t sin6_family;//对应的协议族AF_INET6 in_port_t sin6_port; //16比特的端口 uint32_t sin6_flowinfo;//流信息,未定义 struct in_addr sin6_addr; //32位IPv4地址,使用网络字节序 uint32_t sin6_scope_id; };通用套接字地址结构:
struct sockaddr { uint8_t sa_len;// sa_family_t sa_family; char sa_data[14]; }; //正因为int bind(int,struct sockaddr*,socklen_t); //于是对上述函数调用都必须将指向特定于协议的套接字地址结构的指针进行类型强制转换,如下 struct sockaddr_in serv;//IPv4地址结构 bind(sockfd, (struct sockaddr*)&serv, sizeof(serv));新的通用套接字地址结构:
struct sockaddr_storage {//1,如对套接字结构有对齐的需要,sockaddr_storage能够满足最苛刻的对齐要求 uint8_t ss_len;//2,sockaddr_storage足够大,能够容纳系统支持的任何套接字地址结构 sa_family_t ss_family; };
2、值-结果参数
从进程到内核传递套接字地址结构函数有bind、connect、sendto,这些函数的一个参数是指向某个套接字地址结构的指针,另一个参数是该结构的整数大小。如下:
struct sockaddr_in serv; connect(sockfd, (SA*)&serv, sizeof(serv));从内核到进程传递套接字地址结构函数有accept、recvfrom、getsockname、getpeername,这些函数的一个参数是指向某个套接字地址结构的指针,另一个指向表示该结构大小的整数变量的指针。
struct sockadd_un cli; socklen_t len; len = sizeof(cli);//在调用getpeername函数之前必须初始化len getpeername(unixfd, (SA*)&cli, &len);
引入值-结果参数的原因:当函数被调用时,结构 大小是一个值,它告诉内核该结构的大小,这样内核在写该结构时不至于越界;当函数返回时,结构大小又是一个结果(result),它告诉进程内核在该结构中究竟存储了多少信息。
3、字节排序函数
小端字节序,如0xe00000ff对应“224.0.0255”
大端字节序:大端存储在起始地址上
主机字节序、网络字节序(一般为大端字节序)
两字节序之间的转换函数如下:
#include<netinet/in.h> uint16_t htons(uint16_t host16bitvalue);//host to network short uint32_t htonl(uint32_t host32bitvalue);//host to network long uint16_t ntohs(uint16_t net16bitvalue);//network to host short uint32_t ntohs(uint32_t net32bitvalue);//network to host long
4、字节操作函数
#include<string.h> void bzero(void *dest, size_t nbytes); void *memset(void* dest, int c, size_t len); void bcopy(const void* src, void* dest, size_t nbytes); void memcopy(void* dest, const void* src, size_t nbytes); void bcmp(const void* ptr1, const void* ptr2, size_t nbytes); void memcmp(const void* ptr1, const void* ptr2, size_t nbytes);
5、inet_pton和inet_ntop函数及inet_aton、inet_addr和inet_ntoa函数
#include<arpa/inet.h> //返回:若字符串有效则为1,否则为0 int inet_aton(const char* strptr, struct in_addr* addrptr);//将C字符串转化换成一个32位的网络字节序二进制值 //返回:若字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE in_addr_t inet_addr(const char *strptr);//同上函数转化,但存在一个小BUG,255.255.255.255不能由此函数处理,见书上P67解释 //返回:指向一个点分十进制数串的指针 char* inet_ntoa(struct in_addr inaddr);//将一个32位的网络字节序二进制IPv4地址转换成相应的点分十进制数串。
#include<arpa/inet.h> //以下两函数均由inet_aton、inet_addr、inet_ntoa函数组成,详情见书P69-P70 int inet_pton(int family, const char* strptr, void* addrptr); //inet_pton函数将点分十进制数的表达格式转换成32位二进制数值格式, //返回:若成功则为1,若输入不是有效的表格式则为0,若出错则为-1 const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len); //inet_ntop函数是inet_pton函数的逆向操作, //返回:若成功则为指向结果的指针,若出错则为NULL
6、sock_ntop函数(自定义函数,指向某个套接字地址结构的指针为参数,查看该结构的内部,然后调用适当的函数返回该地址的表达格式)
#include<unp.h> char* sock_ntop(const struct sockaddr* sockaddr, socklen_t addrlen); //该函数为自定义函数,书上通用,避免了与协议有关, //返回:若成功则为非空指针,若出错则为NULL
7、readn、writen和readline函数
#include<unp.h> //读或写一个字节流套接字时使用的函数 ssize_t readn(int filedes, void *buff, size_t nbytes); ssize_t writen(int filedes, const void* buff, size_t nbytes); ssize_t readline(int filedes, void* buff, size_t maxlen);再解释一下,tcp套接字为了应用进程提供了一个字节流,它们没有记录标记。从tcp套接字read的返回值可能比我们请求的数量少,但是这不表示发生了错误。为帮助读或写一个字节流,于是开发了readn、writen和readline这3个函数。