QT中的服务器与客户端

news2024/12/23 23:03:01

一、前言

        本文主要讲讲QT中服务器与客户端的使用方法,QT已经封装好了,调用相应类直接访问即可。本文以QT中的QT中的TCP为例子,讲下使用方法以及线程中使用。

二、正文

2.1 Sever的使用方法

2.1.1 思路

QT中Sever使用的时候大致步骤为:

1、创建监听的服务器对象

2、监听到数据信号,创建QTcpSocket的套接字对象

3、检测是否可以接收数据

4、信息发送,查看数据发送是否正常

总的来说,就是先确定要监听的地址与端口,然后数据在套接字中,套接字在QT中是QTcpSocket

封装的类,直接用就行,最后通过套接字接收数据或者发送数据即可。

2.1.2 代码示例

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QTcpServer *m_server;
    QTcpSocket *m_tcp;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->lineEdit_port->setText("8888");

    //创建监听的服务器对象
    m_server = new QTcpServer(this);
    connect(ui->pushButton_listen, &QPushButton::clicked, this, [=](){
        m_server->listen(QHostAddress::Any, ui->lineEdit_port->text().toInt());
        ui->pushButton_listen->setDisabled(true);
    });

    //监听到数据信号,创建QTcpSocket的套接字对象
    connect(m_server, &QTcpServer::newConnection, this, [=](){
        m_tcp = m_server->nextPendingConnection();

        //检测是否可以接收数据
        connect(m_tcp, &QTcpSocket::readyRead, this, [=](){
            QByteArray data = m_tcp->readAll();
            ui->textEdit_historymsg->append("客户端说: " + data);
        });
    });

    //信息发送
    connect(ui->pushButton_send, &QPushButton::clicked, this, [=](){
        QString msg = ui->textEdit_sendmsg->toPlainText();
        m_tcp->write(msg.toUtf8());
        ui->textEdit_historymsg->append("服务器说: " + msg);
    });

}

Widget::~Widget()
{
    delete ui;
}

2.1.3 细节讲解

创建服务器对象QTcpServer,对象有监听的方法listen。

监听的地址设置所有即可访问你电脑可以收到的所有地址,可以用win+R进入cmd查看有哪些,比如:

获取到新的连接后,将下一个挂起的连接作为已连接的QTcpSocket对象返回

准备读取信号有了后即可开始读取套接字内的数据。

2.1.4 演示

监听成功

2.2 Client的使用方法

2.2.1 思路

QT中Client使用的时候大致步骤为:

1、创建Socket对象,连接服务器

2、数据交互

3、根据需求断开连接

总体来说很简单,套接字连接好服务器的地址与端口即可,前提是服务器开启了,不然连接会失败。

2.2.2 代码示例

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QTcpSocket *m_socket;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"

#include <QHostAddress>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->lineEdit_address->setText("192.168.2.24");
    ui->lineEdit_connect->setText("8888");

    //创建Socket对象,连接服务器
    m_socket = new QTcpSocket(this);
    connect(ui->pushButton_connect, &QPushButton::clicked, this, [=](){
        m_socket->connectToHost(QHostAddress(ui->lineEdit_address->text()), ui->lineEdit_connect->text().toInt());
        ui->pushButton_connect->setDisabled(true);
        ui->pushButton_close->setEnabled(true);
    });

    //断开连接
    connect(ui->pushButton_close, &QPushButton::clicked, this, [=](){
        m_socket->close();
        ui->pushButton_connect->setEnabled(true);
        ui->pushButton_close->setDisabled(true);
    });

    //数据交互
    connect(m_socket, &QTcpSocket::connected, this, [=](){
        ui->textEdit_history->append("服务器连接成功!!!!");
    });
    connect(m_socket, &QTcpSocket::readyRead, this, [=](){
        ui->textEdit_history->append("服务器说: " + m_socket->readAll());
    });
    connect(m_socket, &QTcpSocket::disconnected, this, [=](){
        ui->textEdit_history->append("服务器断开连接!!!!");
    });
    connect(ui->pushButton_send, &QPushButton::clicked, this, [=](){
        m_socket->write(ui->textEdit_send->toPlainText().toUtf8());
        ui->textEdit_history->append("客户端说: " + ui->textEdit_send->toPlainText().toUtf8());
    });
}

Widget::~Widget()
{
    delete ui;
}

2.2.3 细节讲解

没啥可讲的,QT都封装好了,连接好就能直接用,比串口的请求响应简单方便多了。

2.2.4 演示

注意,先开启服务器,然后才能使用,局域网可以内部使用,外部使用需要申请IP。

2.3 线程中的使用

在实际项目中,数据的部分大多是写进线程的,关于线程的了解在前文中有讲述,需要的朋友可以自行查阅。

下面以文件从客户端传到服务器为例:

2.3.1 代码示例

2.3.1.1 Sever
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpServer>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_listen_clicked();

private:
    Ui::MainWindow *ui;
    QTcpServer* m_server;

};
#endif // MAINWINDOW_H
#ifndef ACCEPTFILE_H
#define ACCEPTFILE_H

#include <QObject>
#include <QThread>
#include <QTcpSocket>

class acceptfile : public QThread
{
    Q_OBJECT
public:
    explicit acceptfile(QTcpSocket* tcp, QThread *parent = nullptr);

signals:
    void over();

protected:
    void run() override;

private:
    QTcpSocket* m_tcp;

};

#endif // ACCEPTFILE_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "acceptfile.h"

#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //创建服务器对象
    m_server = new QTcpServer(this);
    //创建套接字
    connect(m_server, &QTcpServer::newConnection, this, [=](){
        QTcpSocket* tcp = m_server->nextPendingConnection();
        //创建子线程
        acceptfile* subthread = new acceptfile(tcp);
        subthread->start();

        //释放资源
        connect(subthread, &acceptfile::over, this, [=](){
            subthread->quit();
            subthread->wait();
            subthread->deleteLater();
            QMessageBox::information(this, "文件接收", "文件接收完毕!!!!!!!");
        });
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_listen_clicked()
{
    m_server->listen(QHostAddress::Any, ui->lineEdit_port->text().toUShort());
}
#include "acceptfile.h"

#include <QFile>


acceptfile::acceptfile(QTcpSocket* tcp, QThread *parent) : QThread(parent)
{
    m_tcp = tcp;
}

void acceptfile::run()
{
    //创建文件对象,保存客户端发送过来的文件内容
    QFile* file = new QFile("C:/Users/EDY/Desktop/QTtest/recv.txt");
    file->open(QFile::WriteOnly);
    //读取套接字socket的内容
    connect(m_tcp, &QTcpSocket::readyRead, this, [=](){
        static int total = 0;
        static int count = 0;
        if(count == 0)
        {
            m_tcp->read((char*)&total, 4);
        }
        //读出剩余数据
        QByteArray all = m_tcp->readAll();
        count += all.size();
        file->write(all);

        //判断是否接收完毕
        if(total == count)
        {
            m_tcp->close();
            m_tcp->deleteLater();
            file->close();
            file->deleteLater();
            emit over();
        }
    });
    //进入事件循环
    exec();
}
2.3.1.2 Client
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include <QThread>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void startconnect(unsigned short, QString);
    void sendFile(QString path);

private slots:
    void on_connectserver_btn_clicked();

    void on_file_btn_clicked();

    void on_send_btn_clicked();

private:
    Ui::MainWindow *ui;

};
#endif // MAINWINDOW_H
#ifndef SENDFILE_H
#define SENDFILE_H

#include <QObject>
#include <QTcpSocket>

class sendfile : public QObject
{
    Q_OBJECT
public:
    explicit sendfile(QObject *parent = nullptr);

    //连接服务器
    void connectServer(unsigned short address, QString ip);

    //发送文件
    void sendFile(QString path);

signals:
    void connectOK();
    void gameover();
    void curpercent(int);

private:
    QTcpSocket* m_tcp;

};

#endif // SENDFILE_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "sendfile.h"

#include <QFileDialog>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->port->setText("8888");
    ui->IP->setText("192.168.2.24");
    ui->progressBar->setRange(0, 100);
    ui->progressBar->setValue(0);

    //创建线程对象
    QThread* thrd = new QThread;
    //创建任务
    sendfile* worker = new sendfile;
    //将work放入子线程
    worker->moveToThread(thrd);
    //连接服务器
    connect(this, &MainWindow::startconnect, worker, &sendfile::connectServer);
    //连接文件发送
    connect(this, &MainWindow::sendFile, worker, &sendfile::sendFile);
    //处理连接服务器的子线程数据
    connect(worker, &sendfile::connectOK, this, [=](){
        QMessageBox::information(this, "连接服务器",  "连接成功!!!!");
    });
    connect(worker, &sendfile::gameover, this, [=](){
        //资源释放
        thrd->quit();
        thrd->wait();
        worker->deleteLater();
        thrd->deleteLater();
    });
    //处理选择文件发送子线程数据
    connect(worker, &sendfile::curpercent, ui->progressBar, &QProgressBar::setValue);

    //线程开始
    thrd->start();
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_connectserver_btn_clicked()
{
    QString ip = ui->IP->text();
    unsigned short port = ui->port->text().toUShort();
    emit startconnect(port, ip);
}

void MainWindow::on_file_btn_clicked()
{
    QString path = QFileDialog::getOpenFileName();
    if(path.isEmpty())
    {
        QMessageBox::warning(this, "打开文件", "选择的文件路径不能为空!!");
        return;
    }
    ui->file->setText(path);
}

void MainWindow::on_send_btn_clicked()
{
    emit sendFile(ui->file->text());
}
#include "sendfile.h"

#include <QFile>
#include <QFileInfo>

sendfile::sendfile(QObject *parent) : QObject(parent)
{

}

void sendfile::connectServer(unsigned short address, QString ip)
{
    m_tcp = new QTcpSocket;
    m_tcp->connectToHost(ip, address);
    connect(m_tcp, &QTcpSocket::connected, this, &sendfile::connectOK);
    connect(m_tcp, &QTcpSocket::disconnected, this, [=](){
        m_tcp->close();
        m_tcp->deleteLater();
        emit gameover();
    });
}

void sendfile::sendFile(QString path)
{
    QFile file(path);
    QFileInfo info(path);

    int filesize = info.size();

    file.open(QFile::ReadOnly);

    while (!file.atEnd())
    {
        static int num = 0;
        if(num == 0)
        {
            m_tcp->write((char*)&filesize, 4);
        }
        QByteArray line = file.readLine();
        num += line.size();
        int percent = (num * 100 / filesize);
        emit curpercent(percent);

        m_tcp->write(line);
    }
}

2.3.2 代码讲解

没啥好讲的,可以看看代码,写得很详细,文件传输部分跟普通数据也是一样的,区别就是需要用到文件的类QFile和QFileInfo,一个是文件对象,一个是文件信息对象。简单来说指文件是QFile,指文件的具体数据是QFileInfo。源码已上传

2.3.3 演示

QTcp服务器与客户端

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1549871.html

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

相关文章

Python抓取抖音直播间数据:技术探索与实践

目录 一、引言 二、技术准备 三、分析抖音直播间网页结构 四、编写爬虫代码 五、处理反爬虫机制 六、数据清洗与存储 七、总结 一、引言 随着互联网的快速发展&#xff0c;直播行业已成为当下的热门领域。抖音作为其中的佼佼者&#xff0c;吸引了大量的用户和主播。对于…

集合系列(十五) -CopyOnWriteArrayList详解

一、摘要 在介绍 CopyOnWriteArrayList 之前&#xff0c;我们一起先来看看如下方法执行结果&#xff0c;代码内容如下&#xff1a; public static void main(String[] args) {List<String> list new ArrayList<String>();list.add("1");list.add(&quo…

Flutter 常用插件Plugin整理并附带实例

最近有点空闲时间&#xff0c;正好写一篇文章&#xff0c;整理一下我们在Flutter开发中常用的插件Plugin使用并附带上实例。 在日常开发中&#xff0c;整个demo目前应该满足大家所有的开发需求&#xff0c;例如&#xff1a;http请求、列表刷新及加载、列表分组、轮播图、视频播…

如何使用Python结合Pillow、matplotlib和OpenCV实现图片读取

使用Pillow库 matplotlib是一个绘图库&#xff0c;经常用于数据可视化&#xff0c;但它也可以用来展示图片。 from PIL import Image# 读取图片 image Image.open(.jpg)# 展示图片 image.show()使用OpenCV库 OpenCV是一个强大的计算机视觉和机器学习库。它不仅提供了大量的图像…

记录关于智能家居的路程的一个bug___Segmentation fault(段错误)

前言 其实发生段错误的情况有很多&#xff1a; 其实在项目的开发中最有可能的错误就是①和②&#xff0c;考虑到本项目数组用的比较少&#xff0c;所以主要是考虑错误①指针的误用。 有时候错误就是那么离谱&#xff0c;声音也算是一种设备&#xff1f;&#xff1f;&#xff…

Vue使用el-statistic和el-card显示大屏中的统计数据

​ 一、页面内容&#xff1a; <el-row :gutter"20"><el-col :span"6"><el-card class"box-card"><div><el-statisticgroup-separator",":precision"2":value"value2":title"tit…

机器人寻路算法双向A*(Bidirectional A*)算法的实现C++、Python、Matlab语言

机器人寻路算法双向A*&#xff08;Bidirectional A*&#xff09;算法的实现C、Python、Matlab语言 最近好久没更新&#xff0c;在搞华为的软件挑战赛&#xff08;软挑&#xff09;&#xff0c;好卷只能说。去年还能混进32强&#xff0c;今年就比较迷糊了&#xff0c;这东西对我…

JavaEE:网络原理——协议(应用层+传输层)

应用层 协议就是一种约定 应用层&#xff1a;对应应用程序&#xff0c;是程序员打交道最多的一层&#xff0c;调用系统提供的网络api写出的代码都是属于应用层的。应用层有很多现成的协议&#xff0c;但程序员一般用的还是自定义协议 自定义协议要约定好哪些内容&#xff1f…

【pytest、playwright】多账号同时操作

目录 方案实现思路&#xff1a; 方案一&#xff1a; 方案二&#xff1a; 方案实现思路&#xff1a; 依照上图所见&#xff0c;就知道&#xff0c;一个账号是pytest-playwright默认的环境&#xff0c;一个是 账号登录的环境 方案一&#xff1a; 直接上代码&#xff1a; imp…

Unity学习笔记 9.2D射线

下载源码 UnityPackage 1.Ray2D 让小球向右发射射线&#xff1a; Ray2D ray;void Start() {// Ray2D(起点&#xff0c;终点)ray new Ray2D(this.transform.position, Vector2.right);// Debug.DrawLine(起点&#xff0c;终点&#xff0c;颜色&#xff0c;显示时间)Debug.DrawL…

视图的作用

目录 视图的作用 创建视图 为 scott 分配创建视图的权限 查询视图 复杂视图的创建 视图更新的限制问题 更新视图中数据的部门编号&#xff08;视图的存在条件&#xff09; 限制通过视图修改数据表内容 创建只读的视图 复杂视图创建 oracle从入门到总裁:​​​​​​h…

阿里云ECS选型推荐配置

本文介绍构建Kubernetes集群时该如何选择ECS类型以及选型的注意事项。 集群规格规划 目前在创建Kubernetes集群时&#xff0c;存在着使用很多小规格ECS的现象&#xff0c;这样做有以下弊端&#xff1a; 网络问题&#xff1a;小规格Worker ECS的网络资源受限。 容量问题&…

网络链路层之(1)基础概念

网络链路层之(1)基础概念 Author: Once Day Date: 2024年3月27日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 通信网络技术_Once-Day的博客-CSD…

Fastjson配置消息转换器(时间格式问题)

问题&#xff1a; 我们可以看见&#xff0c;日期的格式有点问题。 由于ArticleListVO类的createTime成员变量是Date类型&#xff0c;默认是由java的Jackson来处理&#xff0c;使用 ISO-8601 规范来处理日期时间格式。ISO-8601 是一种国际标准的日期时间表示法&#xff0c;例如&…

『Apisix安全篇』探索Apache APISIX身份认证插件:从基础到实战

&#x1f680;『Apisix系列文章』探索新一代微服务体系下的API管理新范式与最佳实践 【点击此跳转】 &#x1f4e3;读完这篇文章里你能收获到 &#x1f6e0;️ 了解APISIX身份认证的重要性和基本概念&#xff0c;以及如何在微服务架构中实施API安全。&#x1f511; 学习如何使…

【Git篇】复习git

文章目录 &#x1f354;什么是git⭐git和svn的区别 &#x1f354;搭建本地仓库&#x1f354;克隆远程仓库&#x1f6f8;git常用命令 &#x1f354;什么是git Git是一种分布式版本控制系统&#xff0c;它可以追踪文件的变化、协调多人在同一个项目上的工作、恢复文件的旧版本等…

金蝶BI方案治好我的数据分析困难症

结构分析、趋势分析、分布分析、对比分析……这还是大方向的&#xff0c;细分下来还会根据数据类型和具体场景不同而不同&#xff0c;不仅如此&#xff0c;每个月的数据分析需求还可能不同&#xff0c;导致分析量多且复杂&#xff0c;加班加点也忙不过来。但金蝶BI方案就不一样…

servlet开发详解

一、什么是servlet&#xff0c;干什么用的&#xff1f;&#xff1f;&#xff1f; tomcat作为一个web服务器&#xff0c;也称作servlet容器。servlet只有放在web服务器中才能运行&#xff0c;不能独立运行。tomcat这个容器要做三件事&#xff1a;接收请求、处理请求和响应请求。…

VMware ESXi部署macOS Monterey

正文共&#xff1a;1024 字 30 图&#xff0c;预估阅读时间&#xff1a;2 分钟 最早使用黑苹果是在2015年&#xff0c;装在了古老的Acer商务本上&#xff08;老樹發新芽&#xff0c;acer tm 4750g裝黑蘋果&#xff09;&#xff1b;上次安装黑苹果是在两年前&#xff08;VMware…

uniapp写小程序如何实现分包

众所众知小程序上传的过程中对包的大小有限制&#xff0c;正常情况下不允许当个包超过2M&#xff0c;所以需要分包 需要再pages.json这个文件夹中进行配置 "pages": [{"path": "pages/index/index","style": {"navigationBarTit…