QT 学习笔记(十四)

news/2024/11/7 6:45:31/

文章目录

  • 一、TCP/IP 通信过程简介
    • 1. Socket 通信
    • 2. Linux 下的 TCP/IP 通信过程
    • 3. QT 下的 TCP/IP 通信过程
      • 3.1 在 QT 中实现 TCP/IP 服务器端通信的流程
      • 3.2 在 QT 中实现 TCP/IP 客户端通信的流程
  • 二、TCP/IP 通信过程操作实现
    • 1. 服务器端
    • 2. 客户端
  • 三、服务器端和客户端实现代码
    • 1. 主函数 main.c
    • 2. 服务器端头文件 widget.h
    • 3. 服务器端源文件 widget.cpp
    • 4. 客户端头文件 clientwidget.h
    • 5. 客户端源文件 clientwidget.cpp

由于每次代码都是在原有程序上修改,因此除了新建项目,不然一般会在学完后统一展示代码。
提示:具体项目创建流程和注意事项见QT 学习笔记(一)
提示:具体项目准备工作和细节讲解见QT 学习笔记(二)

一、TCP/IP 通信过程简介

1. Socket 通信

  • QT 中提供的所有的 Socket 类都是非阻塞的。
  • QT 中常用的用于 socket 通信的套接字类:
  • QTcpServer:用于 TCP/IP 通信,作为服务器端套接字使用。
  • QTcpSocket:用于 TCP/IP 通信,作为客户端套接字使用。
  • QUdpSocket:用于 UDP 通信,服务器,客户端均使用此套接字。

2. Linux 下的 TCP/IP 通信过程

在这里插入图片描述

3. QT 下的 TCP/IP 通信过程

在这里插入图片描述

3.1 在 QT 中实现 TCP/IP 服务器端通信的流程

  • (1) 创建套接字。
  • (2) 将套接字设置为监听模式。
  • (3) 等待并接受客户端请求:可以通过 QTcpServer 提供的 void newConnection() 信号来检测是否有连接请求,如果有可以在对应的槽函数中调用 nextPendingConnection 函数获取到客户端的 Socket 信息(返回值为 QTcpSocket* 类型指针),通过此套接字与客户端之间进行通信。
  • (4) 接收或者向客户端发送数据,包括如下两种:
  • 接收数据:使用 read() 或者 readAll() 函数。
  • 发送数据:使用 write() 函数。

3.2 在 QT 中实现 TCP/IP 客户端通信的流程

  • (1) 创建套接字。
  • (2) 连接服务器:可以使用 QTcpSocket 类的 connectToHost() 函数来连接服务器。
  • (3) 向服务器发送或者接受数据。

二、TCP/IP 通信过程操作实现

  • 生成一个新的项目,具体步骤过程见提示。
  • 在生成新项目的过程中,我们选择基类为 QWidget ,这是因为 QWidget 当中比较干净,不存在别的东西,而 QMainWindow 虽然也可以实现,但其中存在工具栏,菜单栏,核心控件等东西,比较复杂。

1. 服务器端

  • 首先,我们在 ui 界面布置出服务器端的窗口界面,包含两个按钮(发送 send 和 关闭 close)和两个文本编辑区(分别用来输入和显示),具体界面布局如下图所示。

在这里插入图片描述

  • 同时,我们将上面的文本编辑区设置为 readOnly(只读)。这里需要注意的是只修改上面的文本编辑区,下面的是用来输入信息的。

在这里插入图片描述

  • 我们在 TCP.pro 当中加入 QT += network 代码,以便后续工作可以顺利开展。
  • 通过 QT 提供的 QTcpServer 类实现服务器端的 socket 通信。
  • 只有服务器端需要两个套接字,分别是监听套接字 QTcpServer 和通信套接字 QTcpSocket。
  • 这里要使用 Lambda 表达式,需要提前在 TCP.pro 当中加入 CONFIG += C++11 代码。
  • 我们还需要对两个按钮在 ui 界面进行转到槽函数的操作,并进行代码的编写,由于这里需要服务器端和客户端相结合才可以看到现象,故在此处不过多展示实现现象(主要在客户端展示)。
  • 当我们完成基础代码的编写后,运行程序,直接点击 send 按钮,会产生程序异常结束的现象,具体实现现象如下图所示。

在这里插入图片描述

  • 这是因为当我们按下按钮时 tcpsocket 并没有内容,因此,我们需要在代码当中添加 if 判断,tcpsocket 是否为空,若为空,就直接返回,不进入按钮操作。
  • 同时,当我们断开与客户端的连接后,也需要将 tcpsocket 置为空。

2. 客户端

  • 客户端通过使用 QT 提供的 QTcpSocket 类可以方便的实现与服务器端的通信。
  • 在完成服务器端后,我们进行客户端的编写,客户端只有一个套接字就是通信套接字 tcpsocket。
  • 这里我们不重新开一个项目,而是将两部分放在一块。但是,服务器端和客户端的 ui 界面并不相同,因此,我们需要在添加一个 QT 设计师界面类,对客户端的 ui 界面进行设计。

在这里插入图片描述

  • 界面模板默认 Widget 即可。

在这里插入图片描述

  • 随后,我们在 ui 界面布置出客户端的窗口界面,包含两个标签(分别是服务器端口和服务器 IP )以及他们对应的行编辑区,三个按钮(分别是发送 send ,关闭 close 和连接 connect)和两个文本编辑区(分别用来输入和显示)。
  • 同时,我们将上面的文本编辑区设置为 readOnly(只读)。这里需要注意的是只修改上面的文本编辑区,下面的是用来输入信息的。
  • 其中的服务器端口的行编辑区直接默认值是 8888(与服务器端相对应),服务器 IP 的行编辑区直接默认值是 127.0.0.1(本地连接),具体界面布局如下图所示。

在这里插入图片描述

  • 在完成 ui 界面的设计后,我们先编写按钮 connect ,并测试是否可以与服务器端连接,具体实现现象如下图所示。

在这里插入图片描述

  • 但是,这里存在一个问题,就是客户端自己本身并不知道是否连接成功,对此进行优化,使客户端显示成功和服务器端建立好连接,得到如下的实现现象。

在这里插入图片描述

  • 在确保可以完成服务器端和客户端的连接之后,我们进行信息收发功能的实现(主要包括客户端发送信息和服务器端获取客户端编辑区信息并展现),具体实现现象如下图所示。

在这里插入图片描述

  • 最后,实现断开服务器端和客户端连接的功能,就是无法继续发送信息,但是窗口仍然保存,不会关闭,可再次通信发送。

三、服务器端和客户端实现代码

1. 主函数 main.c

#include "widget.h"
#include <QApplication>
#include "clientwidget.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();CLientWidget w2;w2.show();return a.exec();
}

2. 服务器端头文件 widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpServer>//监听套接字
#include <QTcpSocket>//通信套接字namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = nullptr);~Widget();private slots:void on_buttonsend_clicked();void on_pushButton_2_clicked();private:Ui::Widget *ui;QTcpServer *tcpserver;//监听套接字QTcpSocket *tcpsocket;//通信套接字
};#endif // WIDGET_H

3. 服务器端源文件 widget.cpp

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);tcpserver = NULL;tcpsocket = NULL;//监听套接字,指定父对象,让其自动回收空间tcpserver = new QTcpServer(this);tcpserver->listen(QHostAddress::Any,8888);//定义窗口标题setWindowTitle("服务器:8888");connect(tcpserver,&QTcpServer::newConnection,[=](){//取出建立好连接的套接字tcpsocket = tcpserver->nextPendingConnection();//获取对方的IP和端口,转换为字符串便于阅读QString ip = tcpsocket->peerAddress().toString();qint16 port = tcpsocket->peerPort();QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);ui->textEditread->setText(temp);connect(tcpsocket,&QTcpSocket::readyRead,[=](){//从通信套接字中取出内容QByteArray array = tcpsocket->readAll();ui->textEditread->append(array);});});
}Widget::~Widget()
{delete ui;
}void Widget::on_buttonsend_clicked()
{if(NULL == tcpsocket){return;}//获取编辑区内容QString str = ui->textEditread->toPlainText();//给对方发送数据,使用套接字是 tcpsockettcpsocket->write(str.toUtf8().data());
}void Widget::on_pushButton_2_clicked()
{if(NULL == tcpsocket){return;}//主动和客户端断开连接tcpsocket->disconnectFromHost();tcpsocket->close();tcpsocket = NULL;
}

4. 客户端头文件 clientwidget.h

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H#include <QWidget>
#include <QTcpSocket>//通信套接字namespace Ui {
class CLientWidget;
}class CLientWidget : public QWidget
{Q_OBJECTpublic:explicit CLientWidget(QWidget *parent = nullptr);~CLientWidget();private slots:void on_buttonconnect_clicked();void on_pushButtonsend_clicked();void on_pushButtonclose_clicked();private:Ui::CLientWidget *ui;QTcpSocket *tcpsocket;//通信套接字
};#endif // CLIENTWIDGET_H

5. 客户端源文件 clientwidget.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QHostAddress>CLientWidget::CLientWidget(QWidget *parent) :QWidget(parent),ui(new Ui::CLientWidget)
{ui->setupUi(this);setWindowTitle("客户端");tcpsocket = NULL;//分配空间,指定父对象tcpsocket = new QTcpSocket(this);connect(tcpsocket,&QTcpSocket::connected,[=](){ui->textEditread->setText("成功和服务器端建立好连接");});connect(tcpsocket,&QTcpSocket::readyRead,[=](){//获取对方发送的内容QByteArray array = tcpsocket->readAll();//追加到编辑区中ui->textEditread->append(array);});
}CLientWidget::~CLientWidget()
{delete ui;
}void CLientWidget::on_buttonconnect_clicked()
{//获取服务器IP和端口QString ip = ui->lineEditIP->text();qint16 port = ui->lineEditport->text().toInt();//主动和服务器建立连接tcpsocket->connectToHost(QHostAddress(ip),port);}void CLientWidget::on_pushButtonsend_clicked()
{//获取编辑框内容QString str = ui->textEditwrite->toPlainText();//发送数据tcpsocket->write(str.toUtf8().data());
}void CLientWidget::on_pushButtonclose_clicked()
{//主动和对方断开连接tcpsocket->disconnectFromHost();tcpsocket->close();
}

http://www.ppmy.cn/news/10359.html

相关文章

智公网:这几类人员报考公务员无法被录取

对于很多人来讲&#xff0c;毕业之后都想找一份稳定的工作&#xff0c;目前最受欢迎的就是公务员和教师编了&#xff0c;我们今天来说下公务员这个铁饭碗。 公务员考试可是很不容易的&#xff0c;有两场考试&#xff0c;第一场是笔试&#xff0c;第二场是面试。笔试通过之后才…

GPS北斗卫星同步时钟(NTP时间服务器)助力某局指挥中心

GPS北斗卫星同步时钟&#xff08;NTP时间服务器&#xff09;助力某局指挥中心 GPS北斗卫星同步时钟&#xff08;NTP时间服务器&#xff09;助力某局指挥中心 以大数据为核心的数据服务层&#xff0c;围绕各警种业务需求&#xff0c;实现时空关联、综合研判、立体防控等各类实战…

位运算做加法,桶排序找消失元素,名次与真假表示,杨氏矩阵,字符串左旋(外加两道智力题)

Tips 1. 2. 3. 大小端字节序存储这种顺序只有在放进去暂时存储的时候是这样的&#xff0c;但是一旦我里面的数据需要参与什么运算之类的&#xff0c;会“拿出来”先恢复到原先的位置再参与运算&#xff0c;因此&#xff0c;大小端字节序存储的什么顺序不影响移位运算等等…

基于融合SPD+BIFPN+CBAM改进YOLOv5的奶牛检测识别分析系统开发

在我之前的几篇文章中分别应用了不同的tricks来改进yolov5模型如下&#xff1a;加入针对小目标的SPD-Conv模块《yolov5s融合SPD-Conv用于提升小目标和低分辨率图像检测性能实践五子棋检测识别》加入BIFPN特征融合模块《基于yolov5sbifpn实践隧道裂缝裂痕检测》加入注意力机制模…

Go第 12 章 :1-家庭收支记账软件项目

Go第 12 章 &#xff1a;1-家庭收支记账软件项目 12.1 项目开发流程说明 12.2 项目需求说明 模拟实现基于文本界面的《家庭记账软件》该软件能够记录家庭的收入、支出&#xff0c;并能够打印收支明细表 12.3 项目的界面 12.4 项目代码实现 12.4.1 实现基本功能(先使用面向过…

声学特征提取普及笔记

声学特征如何提取? 一、声学特征预处理 首先进行预处理,就是我们最后要提取一帧一帧的这种声学特征,这个语音信号经过预处理然后提取特征,那么特征也有用原始的傅里叶变换,STFT短时傅里叶变换得到这个语谱图特征,也有用FBANK就没有滤波器的输出,还有用MFCC。预处理包括…

Java--经典五道练习题(较难)

文章目录 一、转换罗马数字 二、调整字符串 三、键盘输入任意字符串&#xff0c;打乱里面的内容 四、生成验证码 五、返回两数乘积 一、转换罗马数字 键盘录入一个字符串&#xff0c; 要求1&#xff1a;长度为小于等于9 要求2&#xff1a;只能是数字 将内容变成罗马数…

部署Web项目 (Linux)

部署Web项目 -- Linux一、Linux 环境搭建二、Linux 常用命令三、搭建 Java 部署环境3.1 JDK3.2 Tomcat3.3 MySQL四、部署 Web 项目4.1 什么是部署4.2 数据库建表4.3 构建项目并打包4.4 拷贝到 Tomcat 中4.5 验证一、Linux 环境搭建 这里我们使用的方法是购买云服务器 (CentOS …