QT 学习笔记(十四)

news2025/1/12 8:39:30

文章目录

  • 一、TCP/IP 通信过程简介
    • 1. Socket 通信
    • 2. Linux 下的 TCP/IP 通信过程
    • 3. QT 下的 TCP/IP 通信过程
      • 3.1 在 QT 中实现 TCP/IP 服务器端通信的流程
      • 3.2 客户端通信流程
  • 二、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 客户端通信流程

  • (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_OBJECT

public:
    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();
    //给对方发送数据,使用套接字是 tcpsocket
    tcpsocket->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_OBJECT

public:
    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.coloradmin.cn/o/145946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重装系统重新分区方法教程

对电脑进行系统重装以后&#xff0c;若是想电脑使用起来更稳定舒适&#xff0c;就要对电脑硬盘进行系统分区&#xff0c;但是许多用户都不知道怎样分区&#xff0c;今天小编就来为大家详细介绍一下重装系统重新分区方法教程。 工具/原料&#xff1a; 系统版本&#xff1a;win1…

11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

我工作中用到的几个脚本&#xff0c;主要分为&#xff1a;Python和Shell两个部分。 Python 脚本部分实例&#xff1a;企业微信告警、FTP 客户端、SSH 客户端、Saltstack 客户端、vCenter 客户端、获取域名 ssl 证书过期时间、发送今天的天气预报以及未来的天气趋势图&#xff…

Verilog语法笔记(夏宇闻第三版)-赋值语句和块语句

目录 赋值语句: (1).非阻塞(Non_Blocking)赋值方式( 如 b < a; ): (2).阻塞(Blocking)赋值方式( 如 b a; ): 例&#xff1a; 块语句&#xff1a; 顺序块&#xff1a; 例&#xff1a; 并行块&#xff1a; 例&#xff1a; ​编辑 块名&#xff1a; 起始时间和结束…

php学习笔记-php运算符,类型转换,打印输出语句相较于其他语言的特殊部分-day02

php运算符&#xff0c;类型转换&#xff0c;打印输出语句相较于其他语言的特殊部分php运算符php的类型转换php打印输出语句php运算符 1.php运算符与其他高级语言相同的部分 算术运算符&#xff08;&#xff0c;-&#xff0c;*&#xff0c;/&#xff0c;%&#xff09;&#xff0…

使用Flask快速部署PyTorch模型

对于数据科学项目来说&#xff0c;我们一直都很关注模型的训练和表现&#xff0c;但是在实际工作中如何启动和运行我们的模型是模型上线的最后一步也是最重要的工作。 今天我将通过一个简单的案例&#xff1a;部署一个PyTorch图像分类模型&#xff0c;介绍这个最重要的步骤。 …

OAK-PoE设备故障排查详解

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

Linux小黑板(5):磁盘与文件系统

"这月蹦迪没我" 一、浅谈磁盘 磁盘作为计算机硬件里&#xff0c;唯一的机械设备。在数据存储的领域&#xff0c;有着不可磨灭的作用。相较于&#xff0c;保持通电情况下具有保持数据的不丢失的内存&#xff0c;磁盘能够"永久性"地存储 数据。 磁盘是计算机…

1. R语言介绍、Rstudio的基本使用、【R包的安装与使用】、帮助命令、内置数据集

课程视频链接&#xff1a;https://www.bilibili.com/video/BV19x411X7C6?p1 本笔记参照该视频&#xff0c;笔记顺序做了些调整【个人感觉逻辑顺畅】&#xff0c;并删掉一些不重要的内容 系列笔记目录【持续更新】&#xff1a;https://blog.csdn.net/weixin_42214698/category_…

【Linux】进程控制:父进程与子进程

目录1 进程创建1.1 认识fork1.2 进程创建的目的1.3 写时拷贝1.4 进程创建失败的场景2 进程退出2.1 进程退出状态2.2 进程退出的方式2.3 exit、_exit、return2.3.1 概念2.3.2 区别3 进程等待3.1 理解进程等待3.2 进程等待的方式3.2.1 wait和waitpid3.2.2 status位图结构3.3 阻塞…

【一起从0开始学习人工智能0x04】python相关AI复习【超全面】【收藏】

基础小结 数据类型&#xff1a;Numbers、String、list、tuple、dict字符编码&#xff1a;可以直接.encode&#xff08;‘utf-8’&#xff09;、.decode(ascii)循环&#xff1a;for-in迭代器、whilelist列表-最频繁的数据类型。-完成大多数集合类的数据结构实现。它支持字符&am…

十六、状态管理——Vuex(4)

本章概要 表单处理Vuex 与组合 API模块 16.8 表单处理 在表单控件上通常会使用 v-model 指令进行数据绑定&#xff0c;如果绑定的数据是 Vuex 中的状态数据&#xff0c;就会遇到一些问题。看以下代码&#xff1a; form.html <div id"app"><my-component&…

一场衍生于产业的深度变革正在上演,总结起来就是产业互联网的变革

毫无疑问的是&#xff0c;一场衍生于产业的深度变革正在上演。在这样一场深度变革之中&#xff0c;曾经看似无法改变的存在&#xff0c;有了进化的可能性&#xff1b;曾经让玩家们望而却步的领域&#xff0c;有了进军的可能性。如果对这样一场深度变革进行一次总结的话&#xf…

vanishing point detection in autopilot

1. 概述 消失点一种直观的解释是图像中的平行线的交点&#xff0c;也就如下图中路面边界绘制的直线在图像中的交点。 这样的点在自动驾驶场景下可以为解析车辆状态提供一些信息&#xff0c;比如较为常规的运用便是用于车辆的pitch角度。在传统方法中会通过如霍夫算子检测图片…

2022稳定学习年度研究进展系列报告丨精华观点总结

近年来&#xff0c;在独立分布假设的前提下&#xff0c;机器学习模型的表现越来越好。但在实际应用场景中&#xff0c;数据本身却具有很强的异质性和差异性&#xff0c;这就对模型的泛化能力产生了较高的要求。为了解决分布外泛化问题&#xff0c;稳定学习应运而生。12月28日&a…

Python计算机视觉:人脸识别

讲明一下:并没有实现人脸识别的算法,只是利用人脸特征文件(文件从官网上下载),从而进行人脸识别,总感觉识别出来的效果还是有问题的,如:图片最好是人脸的正脸。 1. 人脸特征文件下载 直接去github或者gitee(建议gitee)上去搜索opencv即可,如下: 选择第一个直接进入即…

一篇文章带你了解——Linux中 文件权限 和 粘滞位的 概念 / 作用 及 实现方法

粘滞位首言用户权限文件权限文件类型分类文件访问者的分类实际解读文件权限文件权限设置方法为啥要有文件权限为啥要有粘滞位粘滞位的好处如何添加粘滞位首言 要了解粘滞位&#xff0c;首先得了解文件及用户权限 用户权限 Linux下有两种用户&#xff1a;超级用户&#xff08…

共享内存原理与使用

共享内存是System V版本的最后一个进程间通信方式。共享内存&#xff0c;顾名思义就是允许两个不相关的进程访问同一个逻辑内存&#xff0c;共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一…

vite学习详解

简介 前言 之前vite2刚出的时候其实已经自学过一波&#xff0c;但是老实说学起来完全不入脑&#xff0c;一方面本来这方面的基础就很差&#xff08;指项目配置&#xff09;&#xff0c;另一方面学的时候没有跟着去动手&#xff0c;纯理论的学那完全就是越看越困。最后就是急躁…

java书店带商家商城书店多单商书店系统源码

简介 Java ssm开发的多商家书店商城&#xff0c;用户可以浏览商品&#xff0c;加入购物车&#xff0c;直接下单支付&#xff0c;在我的个人中心里可以管理自己的订单&#xff0c;收货地址&#xff0c;编辑资料等&#xff0c;还可以申请开店&#xff0c;店铺开通后可以发布商品…

C++--list

前言 这篇文章对于理解封装是非常有帮助的&#xff0c;list的底层是双向链表结构&#xff0c;我们在学习数据结构是就已经学过了双向链表&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前一个元素和后一个元素。因为list独特的结…