基础知识
sockaddr_in与sockaddr
struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址。
sockaddr:在头文件#include<sys/socket.h>中定义,socaddr的缺陷是sa_data把目标地址和端口信息混在一起:
struct sockaddr {
sa_family_t sin_family;//地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};
sockaddr_in:sockaddr_in在头文件#include<netinet/inet.h>或#include<arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr分开存储在两个变量中,如下:
struct sockaddr_in {
sa_family_t sin_family;//地址族
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr;//32位IP地址
char sin_zero[8];//填充字节使sockaddr_in与sockaddr保持一样大小
};
struct in_addr{
In_addr_t s_addr;//32位IPV4地址
}
sockaddr_in是internet环境下套接字的地址形式,所以在网络编程中会对sockaddr_in结构体进行操作,来建立所需要的信息,再用类型转化一下就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数,即sockaddr_in用于socket定义与赋值,sockaddr用于函数参数。
函数语句
getaddrindfo()
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
const char *gai_strerror(int errcode);
函数参数:node(hostname,域名或者IP地址;)、service(服务名,可以是十进制端口号,也可以是已定义的服务器名称,如http;)、hints(可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示;)、res(函数通过result指针参数返回一个指向addrinfo结构体链表的指针,结构体内存储着返回的相关信息;) ;
函数返回值:0——成功,非0——出错;
addrinfo结构体:
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
freeaddrinfo()
由getaddrinfo返回的存储空间,包括addrinfo结构、ai_addr结构和ai_canonname字符串,都是用malloc动态获取的。这些空间可调用 freeaddrinfo释放。其原型如下:
#include <netdb.h>
void freeaddrinfo (struct addrinfo*ai);
ai指向getaddrinfo返回的第一个addrinfo结构。在该链表中的所有结构,以及这些结构所指向的动态存储空间都被释放。假设我们调用getaddrinfo,顺着addrinfo结构链表找到所需的结构,然后只复制该addrinfo结构以保存其信息,再调用 freeaddrinfo,就会产生一个潜藏的错误。原因是addrinfo结构中的指针指向动态分配的内存。因此由我们保存的结构指向的内存在调用 freeaddrinfo后就释放,可能将作它用。只复制addrinfo结构,而不复制addrinfo结构所指向的其他结构,叫做浅拷贝或浅复制。复制addrinfo结构,同时复制addrinfo结构所指向的其他结构,称为深拷贝或深复制.
getifaddrs()
int getifaddrs(struct ifaddrs **ifap);
函数功能: getifaddrs()函数用于获取系统中所有网络接口的信息,返回一个链表,链表中的每一个节点包含一个struct ifaddrs结构,该结构定义在include<ifaddrs.h>头文件中。
函数参数:参数ifap是一个指向struct ifaddrs结构体的指针,当函数成功执行后,ifap将指向一个链表,链表中包含了系统中所有网络接口的信息。
函数返回值:如果成功,返回值0,发生错误,返回值-1,并设置errno变量以只是错误类型。
struct ifaddrs结构体
struct ifaddrs {
struct ifaddrs *ifa_next; // 指向下一个接口信息结构的指针
char *ifa_name; // 接口名称
unsigned int ifa_flags; // 接口标志
struct sockaddr *ifa_addr; // 接口地址
struct sockaddr *ifa_netmask; // 接口子网掩码
// 其他成员...
};
freeifsddrs()为释放getifaddrs()申请的内存
memcmp、memchr、memcpy、memset
处理字符串函数,为标准C库<string.h>;
- memchr()查找内存内容
void* memchr(const void* buf,int ch,size_t count)
功能:从buf所指内存区的前count个字节查找字符ch,当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回null - memcmp 内存比较
int memcmp(const void* buf1,const void* buf2,unsigned int count)功能:比较buf1和buf2的前count个字节;若buf1<buf2,返回值<0;buf1=buf2,返回值=0;buf1>buf2,返回值>0 - memcpy 拷贝内存内容
void * memcpy ( void * destination, const void * source, size_t num );
功能:将num字节的值从源指向的位置直接复制到destination指向的内存块。如果两个位置出现重叠,其行为未定义。返回值为返回目的地。 - memset 设置内存内容
void * memset ( void * ptr, int value, size_t num );功能:将ptr所指向的某一块内存中的前num个字节全部设置成value制定的ASCII值,块的大小由第三个参数制定,这个函数通常为新申请的内存做初始化工作;返回ptr。
inet_pton()\inet_ntop()网络地址转化函数
这两个函数是随着IPV6出现的函数,对于IPV4和IPV6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常都是ASCII字符串,数值格式则是存放在套接字地址结构的二进制值。
#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
(1)这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT. (2)第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0. (3)inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。 blog.csdn.net/zyy61753275…
主机字节序(HBO--Host Byte Order)
字节序:超过一个字节的数据类型在内存中存储的顺序;
大端字节序(Big Endian):高位字节数据存放在内存低地址处,低位字节数据存放在内存高地址处;
小端字节序(Little Endian):高位字节数据存放在内存高地址处,低位字节数据存放在内存低地址处;
地址从左到右为低到高,大端字节序与人类读写数值的方式相同;小端字节序读写数值方式正好相反。
字节序的适用场景
- 计算机正常的内存增长方式是从低到高(当然栈不是),取数据方式是从基址根据偏移找到他们的位置。
- 从大/小端的存储方式可以看出,大端存储因为第一个字节就是高位,从而很容易知道它是正数还是负数,对于一些数值判断会很迅速。
- 小端存储 第一个字节是它的低位,符号位在最后一个字节,这样在做数值四则运算时,从低位每次取出相应字节运算,最后直到高位,并且最终把符号位刷新,这样的运算方式会更高效。所以大端和小端有其各自的优势。
网络字节序(NBO--Network Byte Order)
- TCP/IP协议中RFC1700规定适用“大端”字节序作为网络字节序。
- 不使用大端的计算机在发送数据的时候必须要将自己的主机字节序转换为网络字节序(即“大端”字节序),接收到的数据再转换为自己的主机字节序。这样就与CPU、操作系统无关了,实现了网络通信的标准化。
- BSD Socket提供了封装好的转换接口,方便程序员使用。包括从主机字节序到网络字节序的转换函数:htons、htonl;从网络字节序到主机字节序的转换函数:ntohs、ntohl。
htonl()--"Host to Network Long int" 32Bytes
ntohl()--"Network to Host Long int" 32Bytes
htons()--"Host to Network Short int" 16Bytes
ntohs()--"Network to Host Short int" 16Bytes blog.csdn.net/damanchen/a…
ipv6地址形式
IPv6 地址通常表示为 8 组由冒号分隔的 16 位十六进制数字,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。允许通过省略连续的 0 组进行压缩表示,比如上面的地址可以写成2001:db8:85a3:0:0:8a2e:370:7334,如果一组或多组全为 0 ,还可以使用::来进一步压缩,如2001:0000:0000:0000:0000:0000:0000:0001可写成2001::1。
ipv4地址形式
在上述文档中,地址分为 IPv4 地址和 IPv6 地址,端口号通常为无符号 16 位整数(uint16_t)。
端口号
IPv4 地址与端口号的组合常见形式如:192.168.1.1:80 ,其中 192.168.1.1 是 IPv4 地址, 80 是端口号。
IPv6 地址与端口号的组合常见形如:[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080 ,其中 [2001:0db8:85a3:0000:0000:8a2e:0370:7334] 是 IPv6 地址, 8080 是端口号。
www.midlane.top/wiki/pages/…