自Qt5发布7年以后,Qt官方于2020年12月8日正式发布了Qt6,这将是一个里程碑式的版本。Qt 6将是我们Qt 5系列的延续,因此不会对用户造成干扰。但是这个新的版本将拥有更高的灵活性来实现新的特性和功能,和目前的Qt 5系列相比,它能更好地支持当下和未来的需求。
1. 下载安装器从Qt5.15开始,官方网站上已经不再提供离线安装包了,官网描述如下:
We recommend you use the Qt Online Installer for first time installations and the Qt Maintenance Tool for changes to a current install。
也就是说,官方建议我们使用Qt维护工具( Qt Maintenance Tool)在线安装Qt,官方提供的在线安装器地址如下:
1https://download.qt.io/archive/online_installers/
直接跳转,下载在线安装器
根据自己的需求选择一个版本,进入到对应的目录中,这里选择4.1版本进入
可以看到官方提供了不同平 ...
1. 密码1.1 发送者、接收者和窃听者
请想象一个Alice向Bob发送电子邮件的场景。在这个场景中,发出邮件的Alice称为 发送者(sender),而收到邮件的Bob则称为 接收者(receiver)。
在讲解发送者、接收者的概念时,用邮件这个例子会比较便于理解,但实际上发送者和接收者这两个术语的使用范围并不仅仅局限于邮件。当某个人向另一个人发送信息时,发出信息的人称为发送者,而收到信息的人称为接收者。另外,被发送的信息有时也统称为 消息(message)。
Alice向Bob发送邮件
邮件是通过互联网从Alice的计算机发送到Bob的计算机的。在发送邮件时,邮件会经过许多台计算机和通信设备进行中转,在这个过程中,就存在被恶意窃听者(eavesdropper)偷看到的可能性。
Eve(窃听者)看到邮件的内容
窃听者Eve并不一定是人类,有可能是安装在通信设备上的某种窃听器,也可能是安装在邮件软件和邮件服务器上的某些程序。
尽管邮件内容原本应该只有发送者和接收者两个人知道,但如果不采取相应的对策,就存在被第三方知道的风险。
1.2 加密和解密
Alice不 ...
在标准C++没有提供专门用于套接字通信的类,所以只能使用操作系统提供的基于C的API函数,基于这些C的API函数我们也可以封装自己的C++类 C++套接字类的封装。但是Qt就不一样了,它是C++的一个框架并且里边提供了用于套接字通信的类(TCP、UDP)这样就使得我们的操作变得更加简单了(当然,在Qt中使用标准C的API进行套接字通信也是完全没有问题的)。下面,给大家讲一下如果使用相关类的进行TCP通信。
使用Qt提供的类进行基于TCP的套接字通信需要用到两个类:
QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接。
QTcpSocket:通信的套接字类,客户端、服务器端都需要使用。
这两个套接字通信类都属于网络模块network。
1. QTcpServerQTcpServer类用于监听客户端连接以及和客户端建立连接,在使用之前先介绍一下这个类提供的一些常用API函数:
1.1 公共成员函数
构造函数
1QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);
给监听的套接字设置监听
1234567boo ...
在掌握了基于TCP的套接字通信流程之后,为了方便使用,提高编码效率,可以对通信操作进行封装,本着有浅入深的原则,先基于C语言进行面向过程的函数封装,然后再基于C++进行面向对象的类封装。
1. 基于C语言的封装基于TCP的套接字通信分为两部分:服务器端通信和客户端通信。我们只要掌握了通信流程,封装出对应的功能函数也就不在话下了,先来回顾一下通信流程:
服务器端
创建用于监听的套接字
将用于监听的套接字和本地的IP以及端口进行绑定
启动监听
等待并接受新的客户端连接,连接建立得到用于通信的套接字和客户端的IP、端口信息
使用得到的通信的套接字和客户端通信(接收和发送数据)
通信结束,关闭套接字(监听 + 通信)
客户端
创建用于通信的套接字
使用服务器端绑定的IP和端口连接服务器
使用通信的套接字和服务器通信(发送和接收数据)
通信结束,关闭套接字(通信)
1.1 函数声明通过通信流程可以看出服务器和客户端有些操作步骤是相同的,因此封装的功能函数是可以共用的,相关的通信函数声明如下:
123456789101112131415161718192021///////////// ...
Linux
未读1. 背锅侠TCP在前面介绍套接字通信的时候说到了TCP是传输层协议,它是一个面向连接的、安全的、流式传输协议。因为数据的传输是基于流的所以发送端和接收端每次处理的数据的量,处理数据的频率可以不是对等的,可以按照自身需求来进行决策。
TCP协议是优势非常明显,但是有时也会给我们造成困扰,正所谓:成也萧何败萧何。假设我们有如下需求:
客户端和服务器之间要进行基于TCP的套接字通信
通信过程中客户端会每次会不定期给服务器发送一个不定长度的有特定含义的字符串。
通信的服务器端每次都需要接收到客户端这个不定长度的字符串,并对其进行解析
根据上面的描述,服务器在接收数据的时候有如下几种情况:
一次接收到了客户端发送过来的一个完整的数据包
一次接收到了客户端发送过来的N个数据包,由于每个包的长度不定,无法将各个数据包拆开
一次接收到了一个或者N个数据包 + 下一个数据包的一部分,还是很悲剧,无法将数据包拆开
一次收到了半个数据包,下一次接收数据的时候收到了剩下的一部分+下个数据包的一部分,更悲剧,头大了
另外,还有一些不可抗拒的因素:比如客户端和服务器端的网速不一样,发送和接收的数据量也 ...
Linux
未读在手把手教你写C语言线程池中,已经实现了C语言版的线程池,如果我们也学过C++的话,可以将其改为C++版本,这样代码不管是从使用还是从感观上都会更简洁一些。
对这些代码做从C到C++的迁移主要用到了C++三大特性中的封装,因此难度不大,对应C++初学者来说有助于提高编码水平和对面向对象的理解,对于熟练掌握了C++的人来说就是张飞吃豆芽 -- 小菜一碟(so easy)。
关于线程的在此就不再过多阐述,对于前面文章中设计的线程池,按照面向对象的思想进行拆分可以分为两部分(纯属个人见解,有不同的想法也正常):任务队列类 和线程池类。
本文中关于线程池实现和编写步骤相关细节,请观看视频
手把手教你撸一个线程池 - C++版
1. 任务队列1.1 类声明123456789101112131415161718192021222324252627282930313233343536373839404142// 定义任务结构体using callback = void(*)(void*);struct Task{ Task() { function ...
Linux
未读本文中关于线程池实现和编写步骤相关细节,请观看视频
手把手教你撸一个线程池 - C语言版,这里把相关的代码贴出来,以供参考。
1. 线程池原理我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
在各个编程语言的语种中都有线程池的概念,并且很多 ...
配套视频课程已更新完毕,大家可通过以下两种方式观看视频讲解:
关注公众号:爱编程的大丙,或者进入大丙课堂学习。
1. std::futureC++11中增加的线程类,使得我们能够非常方便的创建和使用线程,但有时会有些不方便,比如需要获取线程返回的结果,就不能通过join()得到结果,只能通过一些额外手段获得,比如:定义一个全局变量,在子线程中赋值,在主线程中读这个变量的值,整个过程比较繁琐。C++提供的线程库中提供了一些类用于访问异步操作的结果。
那么,什么叫做异步呢?
我们去星巴克买咖啡,因为都是现磨的,所以需要等待,但是我们付完账后不会站在柜台前死等,而是去找个座位坐下来玩玩手机打发一下时间,当店员把咖啡磨好之后,就会通知我们过去取,这就叫做异步。
顾客(主线程)发起一个任务(子线程磨咖啡),磨咖啡的过程中顾客去做别的事情了,有两条时间线(异步)
顾客(主线程)发起一个任务(子线程磨咖啡),磨咖啡的过程中顾客没去做别的事情而是死等,这时就只有一条时间线(同步),此时效率相对较低。
因此多线程程序中的任务大都是异步的,主线程和子线程分别执行不同的任务,如果想要在主 ...
配套视频课程已更新完毕,大家可通过以下两种方式观看视频讲解:
关注公众号:爱编程的大丙,或者进入大丙课堂学习。
在某些特定情况下,某些函数只能在多线程环境下调用一次,比如:要初始化某个对象,而这个对象只能被初始化一次,就可以使用std::call_once()来保证函数在多线程环境下只能被调用一次。使用call_once()的时候,需要一个once_flag作为call_once()的传入参数,该函数的原型如下:
123// 定义于头文件 <mutex>template< class Callable, class... Args >void call_once( std::once_flag& flag, Callable&& f, Args&&... args );
flag:once_flag类型的对象,要保证这个对象能够被多个线程同时访问到
f:回调函数,可以传递一个有名函数地址,也可以指定一个匿名函数
args:作为实参传递给回调函数
多线程操作过程中,std::call_once()内部的 ...
配套视频课程已更新完毕,大家可通过以下两种方式观看视频讲解:
关注公众号:爱编程的大丙,或者进入大丙课堂学习。
C++11提供了一个原子类型std::atomic<T>,通过这个原子类型管理的内部变量就可以称之为原子变量,我们可以给原子类型指定bool、char、int、long、指针等类型作为模板参数(不支持浮点类型和复合类型)。
原子指的是一系列不可被CPU上下文交换的机器指令,这些指令组合在一起就形成了原子操作。在多核CPU下,当某个CPU核心开始运行原子操作时,会先暂停其它CPU内核对内存的操作,以保证原子操作不会被其它CPU内核所干扰。
由于原子操作是通过指令提供的支持,因此它的性能相比锁和消息传递会好很多。相比较于锁而言,原子类型不需要开发者处理加锁和释放锁的问题,同时支持修改,读取等操作,还具备较高的并发性能,几乎所有的语言都支持原子类型。
可以看出原子类型是无锁类型,但是无锁不代表无需等待,因为原子类型内部使用了CAS循环,当大量的冲突发生时,该等待还是得等待!但是总归比锁要好。
C++11内置了整形的原子变量,这样就可以更方便的使用原子变量了。 ...