前言:
内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容,所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。
引用:
《操作系统实践-基于Linux应用与内核编程》
作者:房胜、李旭健、黄玲、李哲
出版社:清华大学出版社
资源:
教材资源
链接: https://caiyun.139.com/m/i?1A5Ch36dl1whD 提取码:jdQe
课件和电子资料源码
链接: https://caiyun.139.com/m/i?1A5CvEKY07Uzs 提取码:xyv0
参考链接:
Qt 之网络编程_qt网络编程-CSDN博客
[QT_047]Qt学习之表单布局器(QFormLayout)-CSDN博客
Qt中采用多线程实现Socket编程_parent is qnativesocketenhine parrnt's thread is t-CSDN博客
正文
1. 登录窗口源码
参考电子资源中的源码 '' ch10/QChat/qdlglog.in.cpp",完善Qt聊天程序登录页面的源码。
在Qt Creator 点击 qdlglogin.cpp ,进入编辑页面,参考源码示例增加需要引入的头文件
#include "qdlglogin.h"
#include "ui_qdlglogin.h"
//Add by dimon 2024/-0316 14:32, Copy the source code
#include "qdlggchat.h"
#include "common.h"
#include <QMessageBox> //弹出对话框
#include <QHostAddress>
此时发现 Qt Creator 提示错误,说找不到 'QHostAddress'头文件。原因是我们的Qt 项目需要用到 Qt 网络模块,而当前我们还没有引入 Qt 网络模块。
解决方案,参考链接博文,在项目 ".pro"文件中加入一行,在Qt项目中引起 Qt Network 模块,这样Qt Creator 就能找到 "QHostAddress"头文件了。
QT += network
Qt 之网络编程_qt网络编程-CSDN博客文章浏览阅读2.7w次,点赞21次,收藏38次。Qt Network模块提供的类允许编写 TCP/IP clients 和 servers。_qt网络编程https://blog.csdn.net/liang19890820/article/details/52249509
修改 qdlglogin.h 头文件,需要用到 Qt "QTcpSocket" 类,在qdlglogin.h文件头加上对 QtcpSocket的包含。Qt Creator只有在包含对应的头文件后,在Qt Crateor编辑器里才能智能提示补全头文件里的函数,类。
#ifndef QDLGLOGIN_H
#define QDLGLOGIN_H
#include <QDialog>
#include <QTcpSocket> //QTcpSocket头文件
在 qdlglogin.h 中类 class qdlglogin 中加上 private 私有成员,登录页面中需要记录的用户ID,密码,服务器IP,服务器端口号,以及 *tcpSocket指针。
修改 qdlglogin.cpp的类构造函数,在构造qdlglogin登录对话框时,设置‘用户名’和‘密码’文本输入框的'LineEdit'的提示符,和密码输入文本对话框'LineEdit’的回显模式为 'LineEdit::Password'对密码做特殊回显。设置登录对话框的“登录”和“注册”按钮"QPushButton'初始化为非Enable模式,灰化不可以点击。
效果如下图
2. Qt布局器的使用
在设置qdlgchar.ui窗体的布局是,使用到了Qt Creator设计师界面的 Qt 布局器,Qt布局器可以使用的有水平布局,和垂直布局。
[QT_047]Qt学习之表单布局器(QFormLayout)-CSDN博客文章浏览阅读3.8k次,点赞2次,收藏17次。Qt学习之表单布局器(QFormLayout)_qformlayouthttps://blog.csdn.net/kongcheng253/article/details/128781000
Qt 布局器的使用技巧是:
1. 第一种方法,直接拖动Qt设计师界面到布局器到ui设计界面,然后拖动需要的widget组件到布局器。实际操作拖动之后,就会发现使用鼠标拖动窗体小组件widget到布局器的方式分非常容易误操作,总是不能选择到想要的小组件,或者小组件在布局器里自动排序出错等。
2. 第二种方法,是参考如上链接中的方法,先一次选中多个需要布局的窗体小组件,点击Qt ui设计菜单上的“打破布局”,破坏原有的布局;然后点击窗体菜单“水平布局”或者“垂直布局”,所选中的多个小组件就会自动加入到Qt布局器中。
注意:小组件被加入到布局器中之后,小组件的大小就不能调整了。如果想要再次调整小组件的大小,可以先选择小组件所在的父布局器 点击“破坏布局”,去掉布局之后就可以单独调整小组件的大小。调整小组件大小到之后,再把组件加入到Qt布局器。
点击右键“选择全部”会选择全部的窗体小组件, 然后点击“水平布局”,选择到的组件就都被加入到“水平布局器”中。
技巧1: 单击小组件的时候,总是会选择到Qt布局器里的一个小组件,有时间我们是想去选中整个Qt布局器。此时可以在ui设计界面右侧的窗体里用鼠标选择它的上一级布局器,就可以很容易的选择到父布局器。
技巧2:按下Ctrl键,鼠标单击右侧窗口中的布局器1,再单击布局器2,就会一次同时选择多个布局器。
3. 聊天窗口源码
参考源码实现 qdlggchar.cpp 的源码。需要设置的 comboBox , toolButton 等设置发送 txtEdit 组件中的字体的“字体”,“加粗”,“倾斜”,“颜色” 等 button关联的槽函数时,进入Ui设计界面,鼠标右键“转到槽”,实现槽函数(从示例代码中抄过来)。通过抄写示例代码的过程,实现了 qdlggchat.cpp 窗体的中按钮逻辑,也进一步接触和实践使用Qt来编程实现需要的功能。
4. 添加聊天服务器项目
新建项目,“QWidgetApplcation”,选择基类QDiaglog
使用Qt布局器,设计Qt聊天服务器的窗体布局。
参考电子资源中的示例源码,添加文件(类)qmydb.cpp(.h), qserver.cpp(.h), qthread.cpp(.h), quser.cpp(.h)
右键单击"QChatServer"项目,"Add New",“Add C++ Class”,类命名为"qmydb",基类选贼为"QObject",确定,自动生成 qmydb.cpp 源文件和 qmydb.h头文件。
相同的操作,添加"qserver,quser, qthread, qserver” C++ 类的源文件和头文件。
注意: 如果Qt Creator 有多个项目打开,选择“添加到项目”是需要下拉框选择添加到指定的目标Qt项目。如果保持默认可能把文件会添加到不是自己想要的项目中。
4.1 服务器qmydb 数据库连接类
参考电子资源QChatServer源码中 qmydb 类,抄写源码到我们的Qt聊天服务器项目中。
添加头文件时,Qt Creator编辑器提示找不到"QSqlDatabase"头文件,原因项目用到了Qt数据库模块,但还没有把Qt数据库模块引入到我们QChatServer项目中。
如何查看,我们需要引入哪个Qt 数据库模块?
方式是查看Qt提供的帮助手册,在Qt帮助手册中会说明如果我们需要使用某个Qt模块,需要在项目中如何引入该模块。一般是在项目的 “.pro”文件中加上
Qt += xxx(模块名)
点击Qt Creator编辑器左侧菜单栏“帮助”,进入Qt 帮助文档,点击左下的过滤选择下拉框,下拉框里提供了“索引,查抄,书签,打开页面” 这4个选项。如果需要根据关键字搜索Qt 帮助文档,可以下拉框选择“查找”,输入关键字(例如 qsql),就会出现搜索结果,在搜索结果的帮助文档的起始部分,很明确的告诉我们如果书需要使用 Qt Sql模块需要包含头文件"QSqlDatabase",并且工程中需要在QMake中"QT += sql"引入sql模块(修改项目的 .pro文件,在文件结尾加上 QT += xxx)。
Header:
#include <QSqlDatabase>
qmake:
QT += sql
我们QChatServer聊天服务器需要用到Qt Sql模块和Qt Network模块,所以在 QChatServer.pro 文件尾部加上如下的两行,就在我们的聊天服务器项目中引入了Qt sql和network项目。
4.2 服务器qtcpthread 处理tcp连接线程
4.3 服务器qserver类
对照着电子资源中的'c10/QChatServer'把源码,手动的敲到Virtualbox Ubuntu18.04 Qt5.14.2 编辑器之后,编译执行,进行联调 QChat 聊天程序。
在 QChatServer 聊天服务器假面,点击“启动服务” 按钮,聊天服务器开始监听来自聊天客户端的TCP连接,当收到聊天客户端的注册请求时, QChatServer 服务器端查询MySQL数据库,看用户是否已经注册,如果没有注册,就把用户插入到MySQL数据库并返回用户登录成功。
QChatServer 收到 QTcpSocket 连接请求时,新创建一个线程。并在线程里通过监听 socket 的 ' SIGNAL(readyRead()' 信号,并把槽函数‘SLOT(onReadyRead())’ 和这个信号函数'readyRead()'关联起来。当 QTcpSocket 有数据可以读取时,信号函数'readyRead()'被触发,发出信号,槽函数‘onReadyRead()’被执行。
槽函数'onReadyRead()'中连接数据库MySQL成功,并把用户注册名加入到 MySQL db_chat 数据库表 tb_chat 中。
QChatServer执行时,在窗口里打印出错误提示信息。
Current thread is: QThread(0x56053b2b53f0)
tcpSocket wirte QByteArray to socket
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x7fb3b8001b90), parent's thread is QTcpThread(0x56053b6114a0), current thread is QThread(0x56053b2b53f0)
百度搜索这个错误信息 ‘ Parent is QNativeSocketEngine(0x7fb3b8001b90), parent's thread is QTcpThread(0x56053b6114a0) ’,从如下链接的博文里解释了Qt 多线程编程里程序执行打印出该提示信息的原因。
Qt中采用多线程实现Socket编程_parent is qnativesocketenhine parrnt's thread is t-CSDN博客文章浏览阅读9.9k次,点赞5次,收藏14次。本文介绍的是Qt中采用多线程Socket编程,由于工作项目的需要,使用Qt的socket编程。Qt里的example是个不错的教程,但是当我把代码移植到多线程的环境时就出问题了:QObject: Cannot create children for a parent that is in a different thread.由于想要在线程中保留一个socket,以便维持双边通信,因此_parent is qnativesocketenhine parrnt's thread is tcprecv currenthttps://blog.csdn.net/ggggyj/article/details/51132225根据如下链接博客里的提示,我们修改QChatServer/qtcpthread.cpp 的程序源码中的信号-槽关联函数connect(),加上' Qt::DirectConnection '。QChatServer服务器再执行时,在新线程里就不会打印之前的错误提示 信息了。
//connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()), Qt::DirectConnection);