QT5+TCP/IP多线程传输图片
先上实现结果
一、概述
QT中设计TCP/IP通信主要使用QTCPServer和QTCPSocket两个类,功能分为服务器端和客户端,服务器端负责接收图片,客户端发送图片。多线程设计主要有两种方法,一种是通过添加继承于QThread的类,并在此类中实现run()函数为主体业务逻辑。另一种是继承自Qobject的类中实现业务逻辑,实例化以后添加到子线程(使用movetothread()方法,子线程需要在主线程中独立创建)。线程之间通信最好使用信号与槽机制,或者使用指针传递地址,否则容易报出各种错误。
二、多线程设计两种方式
1.简易版本(可以实现的逻辑有限)
mainwindow.cpp
-
m_ser = new QTcpServer(this);//指定父对象在父对象析构时自动析构
-
m_sock = new QTcpSocket(this);
-
...
-
connect(m_ser,&QTcpServer::newConnection,this,[=]()
-
{
-
//得到套接字对象
-
m_sock = m_ser->nextPendingConnection();
-
//实例化子线程对象
-
recvFile* subThread = new recvFile(m_sock);
-
subThread->start();//开启子线程
-
-
}
recvfile.cpp
-
-
recvFile::recvFile(QTcpSocket* tcp,QObject *parent) : QThread(parent)
-
{
-
tcpSocket = tcp;
-
}
-
-
-
-
void recvFile::run()
-
{
-
//实现接收文件逻辑
-
}
2.全面版本
mainwindow.cpp
-
/*多线程相关对象的实例化*/
-
//创建线程对象
-
QThread *m_thread = new QThread;
-
//创建任务对象
-
SendFile* worker = new SendFile;
-
//任务对象移动到子线程中
-
worker->moveToThread(m_thread);//任务对象会在子线程中执行
sendfile.cpp
-
SendFile::SendFile(QObject *parent) : QObject(parent)
-
{
-
-
tcpSocket = new QTcpSocket;
-
...
-
-
}
三、代码实现
1.UI设计
服务器
客户端
2.服务器端代码实现
mainwindow.cpp
-
-
-
-
-
-
MainWindow::MainWindow(QWidget *parent)
-
: QMainWindow(parent)
-
, ui(new Ui::MainWindow)
-
{
-
ui->setupUi(this);
-
ui_Init();//初始化ui
-
m_ser = new QTcpServer(this);//指定父对象在父对象析构时自动析构
-
m_sock = new QTcpSocket(this);
-
-
qDebug()<<"主线程地址为"<<QThread::currentThread();
-
-
-
//建立连接以后
-
connect(m_ser,&QTcpServer::newConnection,this,[=]()
-
{
-
//得到套接字对象
-
m_sock = m_ser->nextPendingConnection();
-
//实例化子线程对象
-
recvFile* subThread = new recvFile(m_sock);
-
subThread->start();//开启子线程
-
ui->startLisenBt->setEnabled(false);
-
ui->closeListenBt->setDisabled(false);
-
//子线程中接收到的头文件信息显示到接收区
-
connect(subThread,&recvFile::headReceived,this,[=](QString buf)
-
{
-
ui->recvEdit->append(buf);
-
});
-
-
//接收文件过程中更新进度条
-
connect(subThread,&recvFile::receivingPercent,ui->progressBar,&QProgressBar::setValue);
-
//接收完毕以后在主线程进行显示
-
connect(subThread,&recvFile::fileReceived,this,[=](QString path)
-
{
-
ui->statusLabel->setText("文件接收完毕");
-
show_picture(path);
-
});
-
}
-
);
-
-
-
-
-
/*
-
//当连接断开时
-
connect(m_sock,&QTcpSocket::disconnected,this,[=]()
-
{
-
m_sock->close();
-
m_sock->deleteLater();
-
m_status->setPixmap(QPixmap(":/red.jpeg").scaled(20,20));
-
});
-
*/
-
-
}
-
-
MainWindow::~MainWindow()
-
{
-
delete ui;
-
}
-
-
-
void MainWindow:: ui_Init()
-
{
-
ui->PortEdit->setText("30201");
-
setWindowTitle("服务器");
-
//监听按钮初始化
-
ui->closeListenBt->setDisabled(true);
-
//进度条初始化
-
ui->progressBar->setRange(0,100);
-
ui->progressBar->setValue(0);
-
//状态栏初始化
-
m_status = new QLabel;
-
m_status->setPixmap(QPixmap(":/red.jpeg").scaled(20,20));
-
ui->statusbar->addWidget(new QLabel("连接状态"));
-
ui->statusbar->addWidget(m_status);
-
}
-
-
//开启监听
-
void MainWindow::on_startLisenBt_clicked()
-
{
-
unsigned short port = ui->PortEdit->text().toUInt();
-
m_ser->listen(QHostAddress::Any,port);//开启监听
-
ui->startLisenBt->setDisabled(true);
-
//如果已经开启监听
-
if(m_ser->isListening() == true)
-
{
-
-
ui->statusLabel->setText("已经开启监听");
-
ui->closeListenBt->setEnabled(true);
-
ui->startLisenBt->setDisabled(true);
-
m_status->setPixmap(QPixmap(":/green.jpeg").scaled(20,20));
-
}
-
}
-
-
-
-
-
void MainWindow::on_sendTextBt_clicked()
-
{
-
QString msg = ui->SendEdit->toPlainText();
-
m_sock->write(msg.toUtf8());
-
}
-
-
-
/*打开指定路径照片并显示*/
-
void MainWindow::show_picture(QString &filepath)
-
{
-
if(image.load(filepath))//当图像文件导入以后
-
{
-
//显示图像
-
ui->picShow_Label->setPixmap(QPixmap::fromImage(image).scaled(ui->picShow_Label->size()));
-
}
-
}
-
-
void MainWindow::on_closeListenBt_clicked()
-
{
-
-
m_ser->close();
-
if(!m_ser->isListening())
-
{
-
ui->statusLabel->setText("服务器已关闭");
-
ui->startLisenBt->setEnabled(true);
-
ui->closeListenBt->setDisabled(true);
-
}
-
else{
-
QMessageBox::critical(this,"错误","服务器关闭失败");
-
}
-
}
mainwindow.h
-
-
-
-
-
-
-
-
-
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_startLisenBt_clicked();
-
-
void on_sendTextBt_clicked();
-
-
void on_closeListenBt_clicked();
-
-
private:
-
Ui::MainWindow *ui;
-
QTcpServer* m_ser;
-
QTcpSocket* m_sock;
-
QImage image; //接收并显示的图片对象
-
QLabel* m_status;
-
void ui_Init();
-
void show_picture(QString &filepath);
-
};
-
recvfile.cpp
-
-
-
recvFile::recvFile(QTcpSocket* tcp,QObject *parent) : QThread(parent)
-
{
-
tcpSocket = tcp;
-
-
-
}
-
-
void recvFile::run()
-
{
-
qDebug()<<"子线程地址为"<<QThread::currentThread();
-
//初始化文件接收数据
-
fileName = "";
-
fileSize = 0;
-
recvSize = 0;
-
isFile = false;
-
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot()));
-
-
}
-
void recvFile::readyRead_Slot()
-
{
-
QByteArray buf = tcpSocket->readAll();//缓冲区
-
if(isFile==false)
-
{
-
/*先接收文件头*/
-
isFile = true;//下一轮接收时就接收文件本身
-
//文件名字
-
fileName = QString(buf).section("#", 1, 1);
-
//文件大小
-
fileSize = QString(buf).section("#", 2, 2).toInt();
-
-
QString head ="文件名" fileName "\n" "文件大小" QString(buf).section("#", 2, 2);
-
//文件头信息接收到以后发送到主线程进行显示
-
emit headReceived(head);
-
-
recvSize = 0;
-
file.setFileName(fileName);
-
-
if(false == file.open(QIODevice::WriteOnly))
-
{
-
qDebug() <<"文件打开失败";
-
//初始化数据
-
fileName = "";
-
fileSize = 0;
-
recvSize = 0;
-
isFile = false;
-
//QMessageBox::warning(this, "警告", "创建文件失败");
-
return;
-
}
-
-
}
-
-
else//接收文件本身
-
{
-
//ui->receiveEdit->append(QString("正在接收文件%1").arg(fileName));
-
qint64 len = file.write(buf);
-
//qDebug() <<"正在接收文件";
-
recvSize = len;
-
percent = (100 * recvSize ) / fileSize;
-
emit receivingPercent(percent);
-
//qDebug() <<"接收的百分比"<<percent;
-
}
-
if(recvSize == fileSize)//如果接收数据长度和发送数据长度相等做接收后处理
-
{
-
QFileInfo info;
-
-
//qDebug() <<"接收完毕";
-
file.close();
-
info = QFileInfo(file);
-
imgpath = info.filePath();
-
emit fileReceived(imgpath);
-
isFile = false;
-
}
-
-
}
recvfile.h
-
-
-
-
-
-
-
-
-
-
class recvFile : public QThread
-
{
-
Q_OBJECT
-
public:
-
explicit recvFile(QTcpSocket* tcp,QObject *parent = nullptr);
-
private:
-
QTcpSocket *tcpSocket;
-
QFile file; //文件对象
-
QString fileName; //文件名字
-
qint64 fileSize; //文件大小
-
qint64 recvSize; //已接收文件的大小
-
bool isFile; //接收文件标志
-
bool alreadyReceive; //接收完成标志
-
QImage image; //接收并显示的图片对象
-
QString imgpath; //接收到图片的路径
-
int percent; //接收的比例
-
protected:
-
void run() override;
-
private slots:
-
void readyRead_Slot();//接收文件数据的槽函数
-
signals:
-
void headReceived(QString head);//文件头信息接收到以后发送到主线程的接收区显示
-
void fileReceived(QString path);//文件完全接收以后发送文件路径到主线程显示
-
void receivingPercent(int percent);//接收过程中发送接收文件的百分比
-
};
-
-
3.客户端代码实现
mainwindow.cpp
-
-
-
-
-
-
//多线程TCP通信的客户端
-
MainWindow::MainWindow(QWidget *parent)
-
: QMainWindow(parent)
-
, ui(new Ui::MainWindow)
-
{
-
ui->setupUi(this);
-
qDebug()<<"主线程地址为"<<QThread::currentThread();
-
ui->disconnectBt->setDisabled(true);//初始不能断开连接
-
setWindowTitle("客户端");
-
//ip,端口号初始化
-
ui->PortEdit->setText("30201");
-
getIPInfo();//IP、端口号填充
-
//进度条初始化
-
ui->progressBar->setRange(0,100);
-
ui->progressBar->setValue(0);
-
connect_FileThread();//子线程初始化和槽函数连接
-
tab1 = new QTabWidget(this);
-
QWidget *tcp_tab = new QWidget;
-
QWidget *serial_tab = new QWidget;
-
tab1->addTab(tcp_tab,"tcp");
-
tab1->addTab(serial_tab,"ser");
-
//状态栏初始化
-
m_status = new QLabel;
-
m_status->setPixmap(QPixmap(":/red.jpeg").scaled(20,20));
-
ui->statusbar->addWidget(new QLabel("连接状态"));
-
ui->statusbar->addWidget(m_status);
-
}
-
-
MainWindow::~MainWindow()
-
{
-
delete ui;
-
}
-
-
-
//发送文件线程连接
-
void MainWindow::connect_FileThread()
-
{
-
/*多线程相关对象的实例化*/
-
//创建线程对象
-
QThread *m_thread = new QThread;
-
//创建任务对象
-
SendFile* worker = new SendFile;
-
//任务对象移动到子线程中
-
worker->moveToThread(m_thread);//任务对象会在子线程中执行
-
-
//主窗口发送开启连接的信号,接收信号的对象是worker,子线程中SendFile类的connectServer进行工作
-
connect(this,&MainWindow::startConnect,worker,&SendFile::connectServer);
-
-
//主窗口发送关闭连接信号
-
connect(this,&MainWindow::closeConnect,worker,&SendFile::closeConnect);
-
-
//子线程中完成连接发送信号,主线程接收到以后弹出messagebox
-
connect(worker,&SendFile::connected_ok,this,[=]()
-
{
-
//连接以后的状态转换
-
ui->statusLabel->setText("已经连接");
-
//QMessageBox::information(this,"连接服务器","已成功连接");
-
m_status->setPixmap(QPixmap(":/green.jpeg").scaled(20,20));
-
ui->disconnectBt->setEnabled(true);
-
ui->connectBt->setDisabled(true);
-
-
});
-
//子线程中检测到断开连接则释放资源(销毁各种对象)
-
connect(worker,&SendFile::tcp_over,this,[=]()
-
{
-
//ui状态转换
-
m_status->setPixmap(QPixmap(":/red.jpeg").scaled(20,20));
-
ui->disconnectBt->setEnabled(false);
-
ui->connectBt->setDisabled(false);
-
//资源释放
-
//m_thread->quit();
-
//m_thread->wait();
-
//worker->deleteLater();
-
//m_thread->deleteLater();
-
-
});
-
//在子线程中发送文本
-
connect(this,&MainWindow::sendText,worker,&SendFile::sendText);
-
-
//子线程接收到文本,在主线程中更新窗口
-
connect(worker,&SendFile::recv_txt,this,[=](QByteArray buf)
-
{
-
ui->recvEdit->append("服务器发送:" buf);
-
});
-
-
//子线程中发送文件
-
connect(this,&MainWindow::sendFile_Path,worker,&SendFile::sendFile);
-
//更新发送速度
-
//connect(worker,&SendFile::send_Speed,this,[=](double speed)
-
//{
-
// ui->speedLabel->setText(tr("发送速度%1 MB/S").arg(speed,0,'f',2));
-
//});
-
-
//更新进度条
-
connect(worker,&SendFile::curPercent,ui->progressBar,&QProgressBar::setValue);
-
-
//开启子线程
-
m_thread->start();
-
}
-
-
-
void MainWindow::on_sendTextBt_clicked()
-
{
-
QString msg = ui->SendEdit->toPlainText();//从UI中获取发送的信息
-
emit sendText(msg);//给sendfile类发送信号
-
}
-
-
-
void MainWindow::getIPInfo()
-
{
-
QString localHostName = QHostInfo::localHostName();
-
QHostInfo hostinfo = QHostInfo::fromName(localHostName);
-
QList<QHostAddress> listAddress = hostinfo.addresses();
-
if(!listAddress.isEmpty() )
-
{
-
ui->ipEdit->setText(listAddress.at(0).toString());
-
}
-
-
-
}
-
-
-
void MainWindow::on_connectBt_clicked()
-
{
-
QString ip = ui->ipEdit->text();
-
unsigned short port = ui->PortEdit->text().toUInt();
-
emit startConnect(port,ip);
-
}
-
-
void MainWindow::on_disconnectBt_clicked()
-
{
-
-
ui->disconnectBt->setEnabled(false);//断开连接按钮不可用
-
ui->connectBt->setDisabled(false);//连接按钮可用
-
emit closeConnect();//发送断开连接信号
-
-
}
-
-
void MainWindow::on_selectFileBt_clicked()
-
{
-
QString path = QFileDialog::getOpenFileName();
-
ui->filePathEdit->setText(path);
-
//获取文件信息
-
QFileInfo info(path);
-
fileSize = info.size(); //文件大小
-
fileName = info.fileName();//文件名
-
-
qDebug()<<"fileSize:"<<fileSize<<endl;
-
ui->statusLabel->setText(tr("打开 %1 文件成功!").arg(fileName));
-
-
if(image.load(path))//当图像文件导入以后
-
{
-
//显示图像
-
ui->picShow_Label->setPixmap(QPixmap::fromImage(image).scaled(ui->picShow_Label->size()));
-
}
-
-
}
-
-
void MainWindow::on_sendFileBt_clicked()
-
{
-
emit sendFile_Path(ui->filePathEdit->text());
-
}
mainwidow.h
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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_sendTextBt_clicked();
-
-
void on_connectBt_clicked();
-
-
void on_disconnectBt_clicked();
-
-
void on_selectFileBt_clicked();
-
-
void on_sendFileBt_clicked();
-
-
private:
-
Ui::MainWindow *ui;
-
QTabWidget *tab1;
-
QImage image;//图像文件
-
QString fileName;//图片名
-
qint64 fileSize;//文件大小
-
QTcpSocket* m_sock;
-
void getIPInfo();
-
QLabel* m_status;
-
void connect_FileThread();
-
signals:
-
void startConnect(unsigned short port,QString ip);
-
void closeConnect();//断开连接信号
-
void sendText(QString data);
-
void sendFile_Path(QString path);
-
};
-
sendfile.cpp
-
-
-
-
-
SendFile::SendFile(QObject *parent) : QObject(parent)
-
{
-
-
tcpSocket = new QTcpSocket;
-
//当有文本数据接收到时发射recv_txt信号给主窗口
-
connect(tcpSocket,&QTcpSocket::readyRead,this,[=]()
-
{
-
QByteArray buf = tcpSocket->readAll();
-
emit recv_txt(buf);
-
});
-
//断开连接发送tcp_over
-
connect(tcpSocket,&QTcpSocket::disconnected,this,[=]()
-
{
-
tcpSocket->close();
-
//tcpSocket->deleteLater();
-
emit tcp_over();
-
});
-
-
}
-
-
void SendFile::connectServer(unsigned short port, QString ip)
-
{
-
qDebug()<<"子线程地址为"<<QThread::currentThread();
-
//tcpSocket = new QTcpSocket;
-
tcpSocket->connectToHost(QHostAddress(ip),port);//连接操作
-
//已经连接发送connnected_ok
-
connect(tcpSocket,&QTcpSocket::connected,this,&SendFile::connected_ok);
-
-
}
-
-
void SendFile::closeConnect()
-
{
-
tcpSocket->disconnectFromHost();
-
tcpSocket->close();
-
}
-
-
void SendFile::sendFile(QString path)
-
{
-
QElapsedTimer timer;//定时器记录程序执行使用时间
-
QFile file(path);
-
QFileInfo info(path);
-
qint64 fileSize = info.size();
-
QString fileName = info.fileName();
-
file.open(QFile::ReadOnly);
-
timer.start();
-
while (!file.atEnd())
-
{
-
if(num == 0)
-
{
-
QString head = QString("head#%1#%2").arg(fileName).arg(fileSize);
-
tcpSocket->write(head.toUtf8().data());
-
tcpSocket->waitForBytesWritten(); //等待数据发送完毕
-
}
-
QByteArray line = file.readLine();
-
num =line.size();
-
//qDebug()<<"已发送文件大小"<<num;
-
//sendSpeed = num / ( milsec * 1024 ) ;//发送速度
-
//qDebug()<<"发送速度"<<sendSpeed;
-
//emit send_Speed(sendSpeed);//向主线程发送速度
-
qint64 percent = (num * 100 /fileSize);
-
emit curPercent(percent);
-
tcpSocket->write(line);
-
}
-
//文件发送完成以后的善后工作
-
num = 0;
-
int milsec = timer.elapsed();
-
-
qDebug()<<"发送消耗时间"<<milsec<<"毫秒";
-
file.close();
-
}
-
-
void SendFile::sendText(QString data)
-
{
-
tcpSocket->write(data.toUtf8());
-
}
-
-
sendfile.h
-
-
-
-
-
-
-
-
-
class SendFile : public QObject
-
{
-
Q_OBJECT
-
public:
-
explicit SendFile(QObject *parent = nullptr);
-
//连接服务器
-
void connectServer(unsigned short port,QString ip);
-
//与服务器断开连接
-
void closeConnect();
-
//发送文件
-
void sendFile(QString path);
-
//发送文本
-
void sendText(QString data);
-
private:
-
QTcpSocket* tcpSocket ;
-
qint64 num=0;
-
qint64 sendSpeed = 0;
-
signals:
-
void connected_ok();
-
void tcp_over();
-
void recv_txt(QByteArray buf);
-
void curPercent(int percent);
-
void send_Speed(qint64 speed);
-
-
};
-
-
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhhkjbkh
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22