@TOC
参考
<7>[QTCN]图片网络传输工具(服务器端)
说明:图片网络传输工具(服务器端)。
亮点:图片网络传输解码。
实现
PictureTcpServer.pro
QT += network
main.cpp
#include "picturetcpserver.h"
#include <QApplication>
#include <QFont>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
PictureTcpServer w;
QFont font;
font.setPixelSize(25);
w.setFont(font);
w.show();
return a.exec();
}
picturetcpserver.h
#ifndef PICTURETCPSERVER_H
#define PICTURETCPSERVER_H
#include <QWidget>
#include <QtNetwork>
#include <QTcpSocket>
#pragma execution_character_set("utf-8")
QT_BEGIN_NAMESPACE
namespace Ui {
class PictureTcpServer; }
QT_END_NAMESPACE
class PictureTcpServer : public QWidget
{
Q_OBJECT
public:
PictureTcpServer(QWidget *parent = nullptr);
~PictureTcpServer();
void SaveImage(QByteArray ba); //保存图片
void SaveData(QByteArray ba); //保存数据
void DelayTime(int ms);
void ChangeEnable(bool isEnable);
void StartListen(); //开始监听
private slots:
void on_btnStart_clicked();
void on_btnClear_clicked();
void on_btnSend_clicked();
void AcceptConnection(); //建立连接
void ReadMyData(); //接收数据带检验
void ReceiveData(); //普通方式接收数据
void DisplayError(QAbstractSocket::SocketError socketError);
void on_rbtnPT_toggled(bool checked);
private:
Ui::PictureTcpServer *ui;
QTcpServer m_tcpServer;
QTcpSocket *m_pTcpServerConnection;
QStringList m_fileNames;
int m_count; //统计存放图片的张数
QByteArray m_message; //存放从服务器接收到的字符串
quint16 m_basize; //存放文件大小信息
};
#endif // PICTURETCPSERVER_H
picturetcpserver.cpp
#include "picturetcpserver.h"
#include "ui_picturetcpserver.h"
#pragma execution_character_set("utf-8")
PictureTcpServer::PictureTcpServer(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PictureTcpServer)
{
ui->setupUi(this);
ChangeEnable(false);
this->setWindowIcon(QIcon(":/1.ico"));
this->setWindowTitle("图片网络传输工具(服务器端)");
m_count=0;
m_basize=0;//初始化值0
connect(&m_tcpServer,SIGNAL(newConnection()),this,SLOT(AcceptConnection()));
}
PictureTcpServer::~PictureTcpServer()
{
delete ui;
}
//接收数据带检验
void PictureTcpServer::ReadMyData()
{
this->DelayTime(ui->txtTime->text().toInt());//延时等待数据处理完
qDebug()<<"ReadMyData()接收数据带检验";
if(m_pTcpServerConnection==nullptr){
qDebug()<<"m_pTcpServerConnection==nullptr";
return;
}
QDataStream in(m_pTcpServerConnection);
in.setVersion(QDataStream::Qt_4_7);
if (m_basize==0)
{
//判断接收的数据是否有两字节(文件大小信息)
//如果有则保存到basize变量中,没有则返回,继续接收数据
if (m_pTcpServerConnection->bytesAvailable()<(int)sizeof(quint16))
{
return;
}
in>>m_basize;
}
//如果没有得到全部数据,则返回继续接收数据
if (m_pTcpServerConnection->bytesAvailable()<m_basize)
{
return;
}
/*将只读取剩余的数据部分,而不包括文件大小信息;
*因为QDataStream类会按照先进先出(FIFO)的顺序读取数据流中的数据。
*当调用in >> m_basize语句时,它会从数据流中读取指定类型和大小的数据,
*并将其存储在变量m_basize中。而in >> m_message会继续从数据流中读取剩余的数据*/
in>>m_message; //将接收到的数据存放到变量中
ui->txtData->append(m_message);
if (ui->ckbox->isChecked())
{
this->SaveData(m_message);
}
this->SaveImage(m_message);
m_count+=1;
qDebug()<<"m_count = "<<m_count;
ui->labCount->setText(QString("提示:数据大小(%1) 图片张数(%2)").arg(QString::number(m_basize),QString::number(m_count)));
m_basize=0; //重新归位0
m_message.clear(); //清空后重新存储
}
//保存图片
void PictureTcpServer::SaveImage(QByteArray ba)
{
QString timeNow=QDateTime::currentDateTime().toString("yyyyMMddHHmmss");
QString saveFileName=QCoreApplication::applicationDirPath()+tr("/imageto/%1.jpg").arg(timeNow);
QDir folder(QCoreApplication::applicationDirPath());
if (!folder.exists("imageto")) // 检查文件夹是否存在
{
if (!folder.mkpath("imageto")) // 创建文件夹并检查是否成功
{
// 创建文件夹失败,进行适当的错误处理
return;
}
}
QByteArray rdc=qUncompress(QByteArray::fromBase64(ba)); // 解压缩,得到解压后的二进制数据
QImage img;
img.loadFromData(rdc); // 从字节数组加载图像数据
img.save(saveFileName);
}
//保存数据
void PictureTcpServer::SaveData(QByteArray ba)
{
QString timeNow=QDateTime::currentDateTime().toString("yyyyMMddHHmmss");
QString saveFileName=QCoreApplication::applicationDirPath()+tr("/imagedata/%1.txt").arg(timeNow);
QDir folder(QCoreApplication::applicationDirPath());
if (!folder.exists("imagedata")) // 检查文件夹是否存在
{
if (!folder.mkpath("imagedata")) // 创建文件夹并检查是否成功
{
// 创建文件夹失败,进行适当的错误处理
return;
}
}
QString str(ba);
QFile file(saveFileName);
if (file.open(QFile::WriteOnly| QIODevice::Truncate))
{
file.write(str.toLatin1());
file.close();
}
}
//延时等待功能
void PictureTcpServer::DelayTime(int ms)
{
QTime t=QTime::currentTime().addMSecs(ms);//当前时间的基础上增加ms毫秒,得到一个目标时间点t
while(QTime::currentTime()<t)
{
/*处理当前线程的事件队列,并等待最多100毫秒来接收新的事件。
*处理所有类型的事件,包括定时器事件、绘图事件等
*通过不断处理事件,可以确保程序在等待期间仍然能够响应其他事件,以避免程序无响应或阻塞*/
QCoreApplication::processEvents(QEventLoop::AllEvents,100);
}
}
void PictureTcpServer::ChangeEnable(bool isEnable)
{
ui->btnStart->setEnabled(!isEnable);
ui->btnSend->setEnabled(isEnable);
}
//开始监听
void PictureTcpServer::StartListen()
{
if (!m_tcpServer.listen(QHostAddress::Any,ui->txtPort->text().toInt()))
{
ui->labCount->setText(tr("提示:发生错误(%1)").arg(m_tcpServer.errorString()));
m_tcpServer.close(); //关闭TCP服务器,停止接受新的连接请求
return;
}
ui->btnStart->setEnabled(false);
ui->btnSend->setEnabled(false);
ui->labCount->setText(tr("提示:正在监听"));
}
//建立连接
void PictureTcpServer::AcceptConnection()
{
// 获取TCP服务器对象的下一个待处理的连接
m_pTcpServerConnection=m_tcpServer.nextPendingConnection();
connect(m_pTcpServerConnection,&QTcpSocket::readyRead,this,[=](){
if (ui->rbtnPT->isChecked()) //普通方式接收数据
{
ReceiveData();
}
else //数据检验方式接收数据
{
ReadMyData();
}
});
connect(m_pTcpServerConnection,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(DisplayError(QAbstractSocket::SocketError)));
connect(m_pTcpServerConnection,&QTcpSocket::disconnected,this,[=](){
/*为了避免内存泄漏,调用deleteLater()函数以删除不再需要的QAbstractSocket对象;*/
m_pTcpServerConnection->deleteLater();
/*deleteLater()函数的作用是在事件循环处理完当前事件后删除对象,这样可以避免直接在槽函数中删除对象导致的问题,
*而不会立即删除它。*/
});
m_tcpServer.close(); //关闭TCP服务器,停止接受新的连接请求
ui->labCount->setText(tr("提示:客户端连接成功"));
ui->btnSend->setEnabled(true);
}
//普通方式接收数据
void PictureTcpServer::ReceiveData()
{
//this->DelayTime(ui->txtTime->text().toInt());
QByteArray read=m_pTcpServerConnection->readAll();
if (!read.isEmpty())
{
ui->txtData->append(QString::fromUtf8(read));
if (ui->ckbox->isChecked())
{
this->SaveData(read);
}
}
}
void PictureTcpServer::DisplayError(QAbstractSocket::SocketError socketError)
{
/* 调用close()函数后,QAbstractSocket对象进入QAbstractSocket::UnconnectedState状态,
* 表示与客户端的连接已经断开,此时对象将无法进行进一步的通信或更新*/
m_pTcpServerConnection->close();
ui->labCount->setText(QString("提示:断开连接,错误(%1)").arg(m_pTcpServerConnection->errorString()));
this->ChangeEnable(false);
}
void PictureTcpServer::on_btnStart_clicked()
{
this->StartListen();
}
void PictureTcpServer::on_btnClear_clicked()
{
ui->txtData->clear();
}
void PictureTcpServer::on_btnSend_clicked()
{
m_pTcpServerConnection->write(ui->txt1->toPlainText().toUtf8());
if (m_pTcpServerConnection->flush())
{
ui->labCount->setText(tr("提示:发送数据成功"));
}
}
void PictureTcpServer::on_rbtnPT_toggled(bool checked)
{
qDebug()<<"接收方式普通方式 使用="<<checked;
if(checked)
{
if(m_pTcpServerConnection==nullptr)
return;
m_pTcpServerConnection->readAll(); // 读取并丢弃缓冲区中的数据
qWarning() << "缓冲区已清空";
}
}
picturetcpserver.ui
效果
源码
Gitee:05PictureTcpServer[QTCN]图片网络传输工具(服务器端)
模糊知识点
QDataStream
类会按照先进先出(FIFO
)的顺序读取数据流中的数据
- 当调用in >> m_basize语句时,它会从数据流中读取指定类型和大小的数据,并将其存储在变量m_basize中。
- 而in >> m_message会继续从数据流中读取剩余的数据
QTcpServer::close()
关闭TCP服务器,停止接受新的连接请求QTcpSocket::close()
QAbstractSocket对象进入QAbstractSocket::UnconnectedState状态,表示与客户端的连接已经断开,此时对象将无法
进行进一步的通信或更新
QTcpSocket::bytesAvailable()
函数用于判断是否有足够
的字节可供读取,不足则可以继续等待更多数据
//调用bytesAvailable()函数来检查是否已经接收到了完整的数据。
//如果可读取的字节数小于文件大小(m_basize),则返回并继续等待更多数据
if (m_pTcpServerConnection->bytesAvailable()<m_basize)
return;
通过这个例子可见,当时自己开发的Qt广告机客户端(下位机)QTcpSocket::eadyRead()
处理的局限