クライアントはサーバーにファイルを送信し、サーバーはファイルを受信する単純な操作を実行します。
1.サーバー
1. QTcpServer クラスのオブジェクトを作成します
QTcpServer * server = new QTcpServer(this);
2. モニター
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
3. QTcpServer によって送信された newConnection シグナルを受信して、次のステップを実行します。
[signal] void QTcpServer::newConnection()
4. nextPendingConnection メソッドを呼び出してソケットを取得します。
// 通过 this->m_server 调用 nextPendConnection
QTcpSocket * socket = server->nextPendingConnection();
5. [signal] void QIODevice::readyRead() シグナルを通じてクライアントによって送信されたメッセージを受信します。
6. クライアントオフライン [シグナル] void QAbstractSocket::disconnected() シグナル表現
子スレッド クラスを作成し、QThread を継承し、親クラスの run() メソッドをオーバーライドします。
run メソッドでは、ファイルを作成し、クライアントから送信されたファイルを受信し、作成したファイルに書き込みます。
ファイルを受信するときは、まずクライアントから初めて送信されたファイルのサイズを取得します。
クライアントから初めて送信されたファイル サイズを取得する
// 进行接收数据的时候,需要知道客户端发来的文件的大小
// 先将客户端第一次发来的数据的大小读取出来
static int count = 0; // 判断是否是客户端第一次发来的数据
static int total = 0; // 记录文件的大小
if(count == 0)
{
this->m_tcp->read((char*)&total, 4); // 获取文件大小
}
子スレッドクラスを作成し、子スレッドを開始します。
// 创建子线程类对象
MyQThread * myqtread = new MyQThread;
// 启动子线程
myqtread->start();
サーバーコード:
widget.h メインスレッドヘッダーファイル
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_listenBtn_clicked();
private:
// 创建QTcpServer 类的对象
QTcpServer * m_server;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp メインスレッド:
#include "widget.h"
#include "ui_widget.h"
#include "myqthread.h"
#include <QMessageBox>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 设置端口号
ui->port->setText("8989");
// 利用多线程进行链接服务器
// 1. 需要创建一个线程类的子类 ,让其继承Qt中的线程QThread
// 2. 重写父类的run() 方法,在该函数内部编写子线程要处理的具体业务流程
// 3. 在主线程中创建子线程对象,new 一个就可以
// 4. 启动子线程,调用start() 方法
// 实例化QTcpServer 对象
this->m_server = new QTcpServer(this);
// 检验是否接收客户端的连接
connect(this->m_server, &QTcpServer::newConnection, this, [=]()
{
// 获取套接字
QTcpSocket * tcp = this->m_server->nextPendingConnection();
// 创建子线程类对象
MyQThread * myqtread = new MyQThread(tcp);
// 启动子线程
myqtread->start();
// 获取子线程中发来的客户端端口的消息
connect(myqtread, &MyQThread::ClientDisconnect, this, [=]()
{
//弹出对话框提示
QMessageBox::warning(this, "警告", "客户端已断开连接...");
});
// 接收接收完客户端的信号
connect(myqtread, &MyQThread::OverRecveid, this, [=]()
{
//弹出对话框提示
QMessageBox::information(this, "提示", "已接收文客户端发来的数据");
// 关闭套接字
tcp->close();
// 释放
tcp->deleteLater();
// 释放线程
myqtread->quit();
myqtread->wait();
myqtread->deleteLater();
});
});
}
Widget::~Widget()
{
delete ui;
}
// 点击监听按钮 进行监听 按钮转到槽的方式
void Widget::on_listenBtn_clicked()
{
//获取端口号
unsigned short port = ui->port->text().toUShort();
//利用this->m_s 调用listen 进行监听
this->m_server->listen(QHostAddress::Any, port);
}
myqthread.h 子スレッド ヘッダー ファイル
#ifndef MYQTHREAD_H
#define MYQTHREAD_H
//#include <QObject>
#include <QTcpSocket>
#include <QThread>
class MyQThread : public QThread
{
Q_OBJECT
public:
explicit MyQThread(QTcpSocket *tcp, QObject *parent = nullptr);
// 2.重写QThread 类中的受保护成员 run() 方法
protected:
void run();
public:
// 自定义套接字对象 记录主线程传进的套接字对象 tcp
QTcpSocket * m_tcp;
signals:
// 自定义信号 将服务器接收完客户端发来的数据 告诉主线程
void OverRecveid();
// 自定义信号 将客户端断开连接 告诉主线程
void ClientDisconnect();
public slots:
};
#endif // MYQTHREAD_H
myqthread.cpp 子スレッド ファイル
#include "myqthread.h"
#include <QFile>
MyQThread::MyQThread(QTcpSocket *tcp, QObject *parent) : QThread(parent)
{
this->m_tcp = tcp;
}
// 2.重写QThread 类中的受保护成员 run() 方法
void MyQThread::run()
{
// 1.创建文件 打开文件
QFile * file = new QFile("recv.txt");
file->open(QFile::WriteOnly); // 以只写的方式打开文件
// 2.检验是否进行读写
connect(this->m_tcp, &QTcpSocket::readyRead, this, [=]()
{
// 进行接收数据的时候,需要知道客户端发来的文件的大小
// 先将客户端第一次发来的数据的大小读取出来
static int count = 0; // 判断是否是客户端第一次发来的数据
static int total = 0; // 记录文件的大小
if(count == 0)
{
this->m_tcp->read((char*)&total, 4); // 获取文件大小
}
// 将剩下的数据全部读取出来
// 获取客户端发来的数据
QByteArray recvClient = this->m_tcp->readAll(); // 全部接收
// 将读取的数据的量记录到count中
count += recvClient.size();
// 将数据写进文件中
file->write(recvClient);
// 判断服务器是否把客户端发来的数据全部读取完
if(count == total)
{
// 关闭套接字
this->m_tcp->close();
// 释放套接字
this->m_tcp->deleteLater();
// 关闭文件
file->close();
file->deleteLater();
// 自定义一个信号 告诉主线程文件 已接收完毕
emit OverRecveid();
}
});
// 3.检验客户端是否断开连接
connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
{
// 将客户端断开连接 发送给主线程
emit this->ClientDisconnect();
});
// 调用 exec 进入事件循环 阻塞
exec();
}
2. クライアント
1. IP とポート番号をバインドする
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol)
2. サーバーに接続します
[signal] void QAbstractSocket::connected()
3. ソケット経由で write メソッドを呼び出し、サーバーにメッセージを送信します。
qint64 QIODevice::write(const char *data, qint64 maxSize)
4. 切断する
[signal] void QAbstractSocket::disconnected()
マルチスレッドを利用してファイルの選択と送信を実現
マルチスレッドの 2 番目の方法を使用する
1. 新しいクラスを作成し、このクラスを QObject から派生させます
。 2. この新しいクラスにパブリック メンバー関数を追加します。関数本体は、子スレッドで実行するビジネス ロジックです。 3.
メイン スレッドで QThread オブジェクトを作成します。 、これは子スレッドのオブジェクトです。
4. メインスレッドでワーククラスオブジェクトを作成します
。 5. ワーククラスオブジェクトを子スレッドオブジェクトに移動するには、QObject クラスによって提供される moveThread を呼び出す必要があります
。 6. 子スレッドを開始します。そして、 start( ) を呼び出します。 スレッドが開始され、スレッドに移動されたオブジェクトはその時点では動作しませんでした。 7.
ワーク クラスのオブジェクト関数を呼び出して、関数の実行を開始します。この時点では、関数は実行されています。サブスレッド。
クライアントコード:
mythread.h タスク クラス ヘッダー ファイル
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QTcpSocket>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
// 连接服务器
void connectToServer(unsigned short port, QString ip);
// 发送文件
void SendFile(QString path);
private:
// 创建QTcpSocket 类的对象
QTcpSocket * m_socket;
signals:
// 自定义一个信息 告诉主线程 成功连接到服务器
void ConnectOK();
// 自定义一个信号 告诉主线程服务器已断开连接
void gameOver();
// 自定义一个信号 将获取的百分比发送给主线程
void SendPercent(int);
public slots:
};
#endif // MYTHREAD_H
mythread.cpp タスク クラス ファイル
#include "mythread.h"
#include <QFileInfo>
#include <QMessageBox>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
// 连接服务器
void MyThread::connectToServer(unsigned short port, QString ip)
{
// 实例化socket类的对象
this->m_socket = new QTcpSocket(this);
// 尝试与服务器取得连接 绑定IP 和端口号
this->m_socket->connectToHost(ip, port);
// 检验是否成功与服务器取等连接
connect(this->m_socket, &QTcpSocket::connected, this, [=]()
{
emit this->ConnectOK(); // 自定义一个信号 告诉主线程 成功连接上服务器
});
// 检验服务器是否断开连接
connect(this->m_socket, &QTcpSocket::disconnected, this, [=]()
{
this->m_socket->close(); // 关闭套接字
emit this->gameOver(); // 发送信号 告诉主线程 服务器已断开连接
});
}
// 发送文件
void MyThread::SendFile(QString path)
{
// 1.获取文件大小
QFileInfo info(path);
int fileSize = info.size();
// 2.打开文件
QFile file(path);
bool ret = file.open(QFile::ReadOnly);
if(!ret)
{
QMessageBox::warning(NULL, "警告", "打开文件失败");
return; // 退出函数
}
// 判断什么时候读完文件
while(!file.atEnd())
{
// 第一次发送文件的时候 将文件的大小发送给服务器
// 定义一个标记 当标记为0时, 表示第一次发送文件
static int num = 0;
if(num == 0)
{
this->m_socket->write((char*)&fileSize, 4); // 将文件大小发送给服务器
}
// 在循环体中 每次读取一行
QByteArray line = file.readLine();
// 每次发送一次数据,就将发送的数据的量记录下来 用于更新进度条
num += line.size();
// 基于num值 计算百分比
int percent = (num*100/fileSize);
// 将百分比发送给主线程
emit this->SendPercent(percent);
// 将读取的数据通过套接字对象发送给服务器
this->m_socket->write(line);
}
}
widget.h メインスレッドヘッダーファイル
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
signals:
// 自定义一个信号 告诉子线程进行链接服务器
void TellToConnect(unsigned short port, QString ip);
// 自定义一个信号 将选中的文件路径发送给任务类
void SendToFile(QString);
private slots:
void on_connectBtn_clicked();
void on_selectBtn_clicked();
void on_sendBtn_clicked();
private:
QString m_path;
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
ウィジェット.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QThread>
#include "mythread.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 利用多线程实现 选择文件 发送文件
// 利用第二种多线程的方法
// 1.创建一个新的类,让这个类从QObject中派生
// 2.在这个新的类中添加一个公有的成员函数,函数体是我们要子线程中执行的业务逻辑
// 3.在主线程中创建一个QThread对象,这个就是子线程的对象
// 4.在主线程中创建一个工作类的对象
// 5.将工作类对象移动到子线程对象中,需要调用QObject类提供的moveThread方法
// 6.启动子线程,调用start() 这个线程启动了,当时移动到线程中的对象并没有工作
// 7.调用工作类的对象函数,让这个函数开始执行,这个时候是在移动到那个子线程中运行的。
// 1.创建QThread对象
QThread *t = new QThread;
// 2.创建任务类的对象
MyThread * working = new MyThread;
// 3.将任务类对象移动到子线程中
working->moveToThread(t);
// 启动子线程
t->start();
// 4.设置IP 端口号
ui->ip_lineEide->setText("127.0.0.1");
ui->port_lineEdit->setText("8989");
// 5.设置进度条
ui->progressBar->setRange(0, 100); // 进度条的范围
ui->progressBar->setValue(0); // 初始化为0
// 6.更新进度条 通过连接任务类发来的信号 实现
connect(working, &MyThread::SendPercent, ui->progressBar, &QProgressBar::setValue);
// 7.接收任务类发来的成功连接到服务器 信号
connect(working, &MyThread::ConnectOK, this, [=]()
{
QMessageBox::information(this, "提示", "成功连接到服务器");
// 将文件按钮设置成不可用状态
ui->sendBtn->setDisabled(false);
});
// 8.连接任务类发来的断开连接的信号
connect(working, &MyThread::gameOver, this, [=]()
{
QMessageBox::warning(this, "警告", "服务器已断开连接");
//释放支援
t->quit();
t->wait();
t->deleteLater();
working->deleteLater();
// 将文件按钮设置成可用状态
ui->sendBtn->setDisabled(true);
});
// 7.将信号和工作类对象中的任务函数连接
connect(this, &Widget::TellToConnect, working, &MyThread::connectToServer);
// 8.将文件路径发给任务函数
connect(this, &Widget::SendToFile, working, &MyThread::SendFile);
// 9.将发送文件按钮设置成可用状态
ui->sendBtn->setDisabled(true);
}
Widget::~Widget()
{
delete ui;
}
// 连接服务器
void Widget::on_connectBtn_clicked()
{
// 获取ip 和 端口号
QString ip = ui->ip_lineEide->text();
unsigned short port = ui->port_lineEdit->text().toShort();
// 将ip 和 端口号 发送取出
emit this->TellToConnect(port, ip);
// 将发送文件按钮设置成不可用状态
ui->sendBtn->setDisabled(false);
}
// 选中文件
void Widget::on_selectBtn_clicked()
{
m_path = QFileDialog::getOpenFileName(); // 打开文件选择对话框
// 判断选中的对话框不能为空
if(m_path.isEmpty())
QMessageBox::warning(this, "警告", "选中要发送的文件不能为空");
// 将选中的文件路径显示到单行编辑框中
ui->filePath_lineEdit->setText(m_path);
}
// 发送文件
void Widget::on_sendBtn_clicked()
{
// 将选中的文件路径发送给任务类
emit this->SendToFile(m_path);
}
プログラム実行結果:
この記事の特典として、Qt 開発学習教材パッケージ、技術ビデオ (C++ 言語基礎、C++ デザイン パターン、Qt プログラミング入門、QT シグナルとスロット メカニズム、QT インターフェイス開発イメージ描画、QT ネットワーク、QT など) を無料で受け取ることができます。データベースプログラミング、QTプロジェクト実戦、QSS、OpenCV、Quickモジュール、面接での質問など) ↓↓↓↓下記参照↓↓料金受け取りは記事下部をクリック↓↓