Sylar C++高性能服务器学习记录17 【Address模块-知识储备篇】

早在19年5月就在某站上看到sylar的视频了,一直认为这是一个非常不错的视频。
由于本人一直是自学编程,基础不扎实,也没有任何人的督促,没能坚持下去。
每每想起倍感惋惜,遂提笔再续前缘。

为了能更好的看懂sylar,本套笔记会分两步走,每个系统都会分为两篇博客。
分别是【知识储备篇】和【代码分析篇】
(ps:纯粹做笔记的形式给自己记录下,欢迎大家评论,不足之处请多多赐教)
QQ交流群:957100923


Address模块-知识储备篇

一、IP地址的组成(我们只讲解IPV4了解概念扫盲即可,铺展开来讲篇幅太长)

局域网中只有同网段下的主机才能互相访问!!!

组成:IP地址 = 网络号 + 主机号
网络号:网络号相同表示属于同一个子网
主机号:表示子网中的某台主机

以下是一个IPV4的例子:

进制8位8位8位8位
十进制192168101
二进制11000000101010000000101000000001

如表所示,由于没有指定网络号和主机号的分界线,所以网段并不明确

如果192.168 是网络号,10.1就是主机号
如果192.168.10是网络号,1就是主机号

所以我们貌似缺少一个东西来指定这个分界线,否则将无法确定网络号!!!


二、子网掩码

作用:用来标识子网,必须与IP同时存在。
1.子网掩码中必须由连续的1和连续的0组成
2.连续的1表示网络地址,连续的0表示主机地址

例如:
IP:192.168.1.1 
子网掩码:255.255.255.0 ,
那ip对应的的网络号和主机号是多少?
分析:子网掩码换算成二进制:11111111.11111111.11111111.00000000
结论:子网掩码中连续的1表示网络号,连续的0表示主机号,对应到IP上即:192.168.1表示网络号,1表示主机号
进制8位8位8位8位
IP十进制19216811
子网掩码十进制2552552550
IP二进制11000000101010000000000100000001
子网掩码二进制11111111111111111111111100000000
与运算的结果为11000000101010000000000100000000
结果十进制19216810

大家要是这部分知识一直搞不清楚的话,不妨拿起纸和笔,不要吝啬你的草稿纸。
抛开十进制,用二进制全部理解一遍,再转到十进制。
其实我这边建议:如果有人和我一样,属于不太聪明类型的,可以不用去思考十进制
只要把二进制表示模式下搞清楚就可以了,十进制只是一种表现形式而已。


三、~0u 什么意思?

#include <iostream>
int main(int argc,char** argv) {
    std::cout << "~0 = " << ~0<< std::endl;
    std::cout << "~0u = " << ~0u<< std::endl;
    return 0;
}

输出结果:

//默认int类型 
//0的正码为00000000,因为首位为0所以三码一致,
//按位取反为11111111(此时为按位取反后的补码),
//将补码转换为反码为11111110,
//反码再转换为正码输出(首位不变)为10000001也就是-1
~0 = -1
//无符号整型
~0u = 4294967295   <=> 11111111 11111111 11111111 11111111

四、(1<<31)-1 什么意思?

c++中int占四个字节,也就是32位,其中第一位(最高位)为符号为。
(1<<31)表示1左移31位,使得符号位为1,其他位为0。此时为负数
再将左移后的结果减一就使得符号位为0,其他位都为1,【即为int表示的有符号数的最大整数】


五、网络常用结构体

1.ifaddrs

struct ifaddrs {
	struct ifaddrs  *ifa_next;    // 指向列表中下一个结构的指针。该字段在列表的最后一个结构中为 NULL
	char            *ifa_name;   // 接口名称
	unsigned int     ifa_flags;   // 提供有关接口的一些信息的标志
	struct sockaddr *ifa_addr;   // 接口地址
	struct sockaddr *ifa_netmask; // 接口的网络掩码
	union {
	    struct sockaddr *ifu_broadaddr;		// 接口广播地址
	    struct sockaddr *ifu_dstaddr;	    // 点对点目的地址                 
	} ifa_ifu;
	#define              ifa_broadaddr ifa_ifu.ifu_broadaddr
	#define              ifa_dstaddr   ifa_ifu.ifu_dstaddr

	void            *ifa_data;    	// 特定地址族数据的缓冲区
};

2.sockaddr

struct sockaddr {
	ushort  sa_family;	 // 网络协议
	char    sa_data[14];
};

3.sockaddr_in

struct sockaddr_in {
    short            sin_family;   // 网络协议
    unsigned short   sin_port;     // 端口
    struct in_addr   sin_addr;     // ip
    char             sin_zero[8];  
};

struct in_addr {
    unsigned long s_addr;  // 使用 inet_aton() 加载
};

4.sockaddr_in6

struct sockaddr_in6 {
    sa_family_t     sin6_family;   /* 网络协议 */
    in_port_t       sin6_port;     /* 端口 */
    uint32_t        sin6_flowinfo; /* IPv6 流信息 */
    struct in6_addr sin6_addr;     /* ip */
    uint32_t        sin6_scope_id; 
};

struct in6_addr {
    unsigned char   s6_addr[16];   /* IPv6 address */
};     

补充:
uint16 :占2个字节
uint32 :占4个字节

我们发现结构 sockaddr 和 sockaddr_in 字节数完全相同,都是16个字节,所以可以直接强转。
但是结构 sockaddr_in6 有28个字节,为什么在使用的时候也是直接将地址强制转化成
(sockaddr*)类型呢?

这几个结构在作为参数时基本上都是以指针的形式传入的
我们拿函数bind()为例,这个函数一共接收三个参数:
第一个为监听的文件描述符。
第二个参数是sockaddr*类型。
第三个参数是传入指针原结的内存大小。
所以有了后两个信息,无所谓原结构怎么变化,因为他们的头都是一样的,也就是uint16 sa_family,那么我们也能根据这个头做处理。

int bind(int socket_fd, sockaddr* p_addr, int add_size);

5.addrinfo

struct addrinfo {
    int              ai_flags;     // 地址信息标志
    int              ai_family;    // 地址族(AF_INET, AF_INET6, AF_UNSPEC)
    int              ai_socktype;  // 套接字类型(SOCK_STREAM, SOCK_DGRAM)
    int              ai_protocol;  // 协议号(IPPROTO_TCP, IPPROTO_UDP),或0表示任意协议
    socklen_t        ai_addrlen;   // 地址长度
    struct sockaddr *ai_addr;      // 网络地址结构指针
    char            *ai_canonname; // 规范名字(主机名或服务名)
    struct addrinfo *ai_next;      // 指向下一个addrinfo结构的指针
};

通过调用getaddrinfo()函数可以填充并返回一个或多个addrinfo结构,其中包含了特定主机名和服务名对应的可用地址信息。


六、网络常用函数

1.getifaddrs

#include <sys/types.h>
#include <ifaddrs.h>

int getifaddrs(struct ifaddrs **ifap);
/**
* func:函数存储对ifaddrs结构的链表的引用;
* return:成功返回0,失败返回-1;
*/
【注意】: 返回的数据是动态分配的,需要释放;

2.freeifaddrs

#include <sys/types.h>
#include <ifaddrs.h>

void freeifaddrs(struct ifaddrs *ifa);
/**
* func:释放对ifaddrs结构的链表的引用;
*/

3.getnameinfo

#include <sys/socket.h>
#include <netdb.h>

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                       char *host, size_t hostlen,
                       char *serv, size_t servlen, int flags);
/**
* func:将套接字地址转换为相应的主机和服务并返回;
* @param sa:套接字地址结构,保存输入的IP地址和端口号;
* @param salen:sa的长度;
* @param host:调用者分配缓冲区;
* @param hostlen:host长度;
* @param serv:调用者分配缓冲区;
* @param servlen:serv长度
* @param flags:
		NI_NAMEREQD:若无法确定主机名,则返回一个错误;
		NI_DGRAM:基于数据报(UDP)而不是基于流(TCP)的;
		NI_NOFQDN:只返回本地主机的完全限定域名的主机名部分;
		NI_NUMERICHOST:返回主机名的数字形式;
		NI_NUMERICSERV:返回服务地址的数字形式;
		
* return:成功返回0,【节点和服务名称将使用以空字符结尾的字符串填充】;
	失败设置errno:
		EAI_AGAIN:			无法解析该名称, 稍后再试;
		EAI_BADFLAGS:		flags参数的值无效;
		EAI_FAIL:			不可恢复的错误;
		EAI_FAMILY:		无法识别地址族,或指定地址族的地址长度无效
      	EAI_MEMORY:		溢出;
 	 	EAI_NONAME:		名称不能解析所提供的参数
		EAI_OVERFLOW:		溢出;
        EAI_SYSTEM:		系统错误;
*/
【注意】:调用者可以通过提供一个NULL host(或serv)参数或一个零hostlen(或servlen)参数来指定不需要主机名(或不需要服
务名)。 但是,必须请求至少一个主机名或服务名。

七、获取本机网络信息案例

#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    struct ifaddrs *ifaddr, *ifa;
    int family, s;
    char host[NI_MAXHOST];

    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    /* 遍历链表,维护头指针,以便稍后可以释放链表  */
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL)
            continue;

        family = ifa->ifa_addr->sa_family;


        /* 显示接口名称和族 */
        printf("%s  address family: %d%s\n",
                ifa->ifa_name, family,
                (family == AF_PACKET) ? " (AF_PACKET)" :
                (family == AF_INET) ?   " (AF_INET)" :
                (family == AF_INET6) ?  " (AF_INET6)" : "");

        /* For an AF_INET* interface address, display the address */

        if (family == AF_INET || family == AF_INET6) {
            s = getnameinfo(ifa->ifa_addr,
                    (family == AF_INET) ? sizeof(struct sockaddr_in) :
                    sizeof(struct sockaddr_in6),
                    host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
            if (s != 0) {
                printf("getnameinfo() failed: %s\n", gai_strerror(s));
                exit(EXIT_FAILURE);
            }
            printf("\taddress: <%s>\n", host);
        }
    }
    freeifaddrs(ifaddr);
    exit(EXIT_SUCCESS);
}

八、什么是大小端?

关于大端小端名词的由来,有一个有趣的故事,来自于Jonathan Swift的《格利佛游记》:Lilliput和Blefuscu这两个强国在过去的36个月中一直在苦战。战争的原因:大家都知道,吃鸡蛋的时候,原始的方法是打破鸡蛋较大的一端,可是那时的皇帝的祖父由于小时候吃鸡蛋,按这种方法把手指弄破了,因此他的父亲,就下令,命令所有的子民吃鸡蛋的时候,必须先打破鸡蛋较小的一端,违令者重罚。然后老百姓对此法令极为反感,期间发生了多次叛乱,其中一个皇帝因此送命,另一个丢了王位,产生叛乱的原因就是另一个国家Blefuscu的国王大臣煽动起来的,叛乱平息后,就逃到这个帝国避难。据估计,先后几次有11000余人情愿死也不肯去打破鸡蛋较小的端吃鸡蛋。

这个其实讽刺当时英国和法国之间持续的冲突。Danny Cohen一位网络协议的开创者,第一次使用这两个术语指代字节顺序,后来就被大家广泛接受。
在这里插入图片描述

大端存储,是将数据的低位字节放到高地址处,高位字节放到低地址处。
小端存储,是将数据的低位字节放到低地址处,高位字节放到高地址处。

假设有一个十六制数:0x12345678

内存地址小端模式存放内容大端模式存放内容
0x40000x780x12
0x40010x560x34
0x40020x340x56
0x40030x120x78

小端模式 :强制转换数据不需要调整字节内容。
大端模式 :符号位的判定固定为第一个字节,容易判断正负。


九、大小端模式的判断(linux 平台下)

在头文件 <endian.h> 中,
可以使用宏定义 BYTE_ORDER 来判断大小端:
如果值为 LITTLE_ENDIAN ,则为小端。
如果值为 BIG_ENDIAN ,则为大端。


十、大小端模式的转换

头文件<byteswap.h>

  1. bswap_16():将一个 16 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。
  2. bswap_32():将一个 32 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。
  3. bswap_64():将一个 64 位整数从大端字节序转换为小端字节序或从小端字节序转换为大端字节序。

十一、enable_if是什么

enable_if<condition,T>::type;
int a;
//当a==1时,condition为true,那么type为int,所以会声明一个int* a;
typename std::enable_if<a == 1,int>::type *a;  
//a !=1时,condition为true,那么type为double,所以会声明一个double* a;
typename std::enable_if<!(a == 1),double>::type *a;  

在上面的例子中:
当a==1时,第一个的condition为true,所以会声明一个int* a。
但是如果a != 1,那么第二个的condition成立,会声明一个double* a。


今天就写到这吧,博客中不明白的可以搜索相关知识进行深入研究,这里点明了要掌握的基础内容。

【最后求关注、点赞、转发】
QQ交流群:957100923
在这里插入图片描述

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态类型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如类、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其成为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值