參考了網上的源代碼,整理修改成了單獨的類,使用起來非常方便,每個類的使用方法在實現文件的上方用註釋的方便說明了,歡迎交流與反饋。
直接上源碼,先上服務端:
#ifndef TCPUPLOADSERVER_H
#define TCPUPLOADSERVER_H
#include <QObject>
#include <QAbstractSocket>
#include <QTcpServer>
#include <QLabel>
class QTcpSocket;
class QFile;
class TcpUploadServer : public QObject
{
Q_OBJECT
public:
explicit TcpUploadServer(QObject *parent = nullptr);
bool StartServer();
void SetLableStatus(QLabel *lableStatus){m_lableStatus = lableStatus;}
void SetPort(const quint16& nPort){m_nPort = nPort;}
signals:
void begin();
void progress(qint64, qint64);
void finished();
public slots:
void acceptConnection();
void updateServerProgress();
void displayError(QAbstractSocket::SocketError socketError);
private:
//界面相關
QLabel *m_lableStatus;
quint16 m_nPort;
QTcpServer m_tcpServer;
QTcpSocket *m_tcpServerConnection;
qint64 m_totalBytes;
qint64 m_bytesReceived;
qint64 m_fileNameSize;
QString m_fileName;
QFile *m_localFile;
QByteArray m_inBlock;
};
#endif // TCPUPLOADSERVER_H
#include "TcpUploadServer.h"
#include <QtNetwork>
/*
TcpUploadServer *server = new TcpUploadServer(this);
connect(server, SIGNAL(begin()), this, SLOT(beginReceive()));
connect(server, SIGNAL(progress(qint64, qint64)), this, SLOT(receiveProgress(qint64, qint64)));
connect(server, SIGNAL(finished()), this, SLOT(receiveFinished()));
server->SetLableStatus(ui->lb_StatusServer);
server->SetPort(9999);
server->StartServer();
*/
TcpUploadServer::TcpUploadServer(QObject *parent) : QObject(parent)
{
m_nPort = 0;
m_lableStatus = nullptr;
m_tcpServerConnection = nullptr;
m_localFile = nullptr;
connect(&m_tcpServer, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
bool TcpUploadServer::StartServer()
{
emit progress(0, 100);
if(!m_tcpServer.listen(QHostAddress::LocalHost, m_nPort))
{
qDebug()<<m_tcpServer.errorString();
return false;
}
qDebug() << "服務啓動!";
qDebug() << "監聽端口" << m_nPort << "成功!";
m_totalBytes=0;
m_bytesReceived=0;
m_fileNameSize=0;
if (m_lableStatus)
{
m_lableStatus->setText("監聽");
}
return true;
}
void TcpUploadServer::acceptConnection()
{
emit begin();
m_tcpServerConnection=m_tcpServer.nextPendingConnection();
connect(m_tcpServerConnection, SIGNAL(readyRead()), this, SLOT(updateServerProgress()));
connect(m_tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)), this ,SLOT(displayError(QAbstractSocket::SocketError)));
if (m_lableStatus)
{
m_lableStatus->setText("接受連接");
}
//關閉服務器不再監聽,直接進入數據收發
m_tcpServer.close();
}
void TcpUploadServer::updateServerProgress()
{
QDataStream in(m_tcpServerConnection);
in.setVersion(QDataStream::Qt_5_7);
// 如果接收到的數據小於16個字節,保存到來的文件頭結構
if (m_bytesReceived<=sizeof(qint64)*2)
{
if((m_tcpServerConnection->bytesAvailable()>=sizeof(qint64)*2)&&(m_fileNameSize==0))
{
// 接收數據總大小信息和帶路徑的文件名大小信息
in>>m_totalBytes>>m_fileNameSize;
m_bytesReceived +=sizeof(qint64)*2;
}
if((m_tcpServerConnection->bytesAvailable()>=m_fileNameSize)&&(m_fileNameSize!=0))
{
// 接收文件名,並建立文件
in>>m_fileName;
if (m_lableStatus)
{
m_lableStatus->setText(tr("接收文件 %1 …").arg(m_fileName));
}
m_bytesReceived+=m_fileNameSize;
m_localFile = new QFile(m_fileName);
if (!m_localFile->open(QFile::WriteOnly))
{
qDebug() << "server: open file error!";
return;
}
}
else
{
return;
}
}
// 如果接收的數據小於總數據,那麼寫入文件
if(m_bytesReceived<m_totalBytes)
{
m_bytesReceived+=m_tcpServerConnection->bytesAvailable();
m_inBlock = m_tcpServerConnection->readAll();
m_localFile->write(m_inBlock);
m_inBlock.resize(0);
}
emit progress(m_bytesReceived, m_totalBytes);
// 接收數據完成時
if (m_bytesReceived==m_totalBytes){
m_tcpServerConnection->close();
m_localFile->close();
if (m_lableStatus)
{
m_lableStatus->setText(tr("接收文件 %1 成功!").arg(m_fileName));
}
emit finished();
//繼續監聽
StartServer();
}
}
void TcpUploadServer::displayError(QAbstractSocket::SocketError socketError)
{
Q_UNUSED(socketError)
qDebug()<<m_tcpServerConnection->errorString();
m_tcpServerConnection->close();
if (m_lableStatus)
{
m_lableStatus->setText("出現錯誤,可能因爲同時上傳多個文件!");
}
StartServer();
}
接着是客戶端:
#ifndef TCPUPLOADCLIENT_H
#define TCPUPLOADCLIENT_H
#include <QObject>
#include <QLabel>
#include <QLineEdit>
#include <QAbstractSocket>
class QTcpSocket;
class QFile;
class TcpUploadClient : public QObject
{
Q_OBJECT
public:
explicit TcpUploadClient(QObject *parent = nullptr);
void SetLableStatus(QLabel *lableStatus){m_lableStatus = lableStatus;}
void SetHostAndPort(const QString& strHost, const quint16& nPort){m_strHost = strHost; m_nPort = nPort;}
//需要用QLineEdit顯示主機和端口調用
void SetLineEditHostAndPort(QLineEdit *leHost, QLineEdit *lePort){m_leHost = leHost;m_lePort = lePort;}
void SetUploadFilePath(const QString& strFilePath);
//strServerFileName不能爲空 用於指定要存儲到服務器上的文件名,可以與本地文件名不同
void StartUpload(const QString& strLoaclPath, const QString& strServerFileName);
//獲取文件名,包含後綴
static QString GetNameOfFile(const QString& strFullPath){return strFullPath.right(strFullPath.size()-strFullPath.lastIndexOf('/')-1);}
signals:
void progress(qint64, qint64);
void finished();
public slots:
void send();
void startTransfer();
void updateClientProgress(qint64);
void displayError(QAbstractSocket::SocketError);
private:
//界面相關
QLineEdit *m_leHost;
QLineEdit *m_lePort;
QLabel *m_lableStatus;
QString m_strHost;
quint16 m_nPort;
QTcpSocket *m_tcpClient;
QFile *m_localFile;
qint64 m_totalBytes;
qint64 m_bytesWritten;
qint64 m_bytesToWrite;
qint64 m_payloadSize;
QString m_fileName;
QString m_strServerFileName;
QByteArray m_outBlock;
};
#endif // TCPUPLOADCLIENT_H
#include "TcpUploadClient.h"
#include <QtNetwork>
/*
TcpUploadClient *tuc = new TcpUploadClient(this);
connect(tuc, SIGNAL(progress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
connect(tuc, SIGNAL(finished()), this, SLOT(uploadFinished()));
tuc->SetHostAndPort("localhost", 9999);
tuc->SetLineEditHostAndPort(ui->lineEditUrl, ui->lineEditPort);
tuc->SetLableStatus(ui->lb_StatusClient);
tuc->StartUpload(strFilePath, tuc->GetNameOfFile(strFilePath));
*/
TcpUploadClient::TcpUploadClient(QObject *parent) : QObject(parent)
{
m_payloadSize=64*1024;
m_totalBytes=0;
m_bytesWritten=0;
m_bytesToWrite=0;
m_tcpClient=new QTcpSocket(this);
connect(m_tcpClient, SIGNAL(connected()), this,SLOT(startTransfer()));
connect(m_tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(updateClientProgress(qint64)));
connect(m_tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
m_nPort = 0;
m_leHost = nullptr;
m_lePort = nullptr;
m_lableStatus = nullptr;
}
void TcpUploadClient::SetUploadFilePath(const QString &strFilePath)
{
if (strFilePath.length())
{
m_fileName = strFilePath;
if (m_lableStatus)
{
m_lableStatus->setText(QString("打開文件 %1 成功!").arg(m_fileName));
}
}
}
void TcpUploadClient::StartUpload(const QString &strLoaclPath, const QString &strServerFileName)
{
//存儲在服務器上的地址
m_strServerFileName = strServerFileName;
SetUploadFilePath(strLoaclPath);
send();
}
void TcpUploadClient::send()
{
m_bytesWritten=0;
if (m_lableStatus)
{
m_lableStatus->setText("連接中...");
}
emit progress(0, 100);
m_tcpClient->connectToHost(m_strHost, m_nPort);
}
void TcpUploadClient::startTransfer()
{
m_localFile=new QFile(m_fileName);
if(!m_localFile->open(QFile::ReadOnly)){
qDebug()<<"client:open file error!";
return;
}
m_totalBytes=m_localFile->size();
QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_5_7);
//文件總大小、文件名大小、文件名
sendOut<<qint64(0)<<qint64(0)<<m_strServerFileName;
m_totalBytes+=m_outBlock.size();
sendOut.device()->seek(0);
sendOut<<m_totalBytes<<qint64(m_outBlock.size()-sizeof(qint64)*2);
m_bytesToWrite=m_totalBytes-m_tcpClient->write(m_outBlock);
if (m_lableStatus)
{
m_lableStatus->setText("已連接");
}
m_outBlock.resize(0);
}
void TcpUploadClient::updateClientProgress(qint64 numBytes)
{
m_bytesWritten+=(int)numBytes;
if(m_bytesToWrite>0)
{
m_outBlock=m_localFile->read(qMin(m_bytesToWrite,m_payloadSize));
m_bytesToWrite-=(int)m_tcpClient->write(m_outBlock);
m_outBlock.resize(0);
}
else{
m_localFile->close();
}
//發送進度
emit progress(m_bytesWritten, m_totalBytes);
if(m_bytesWritten==m_totalBytes)
{
if (m_lableStatus)
{
m_lableStatus->setText(QString("傳送文件 %1 成功").arg(m_fileName));
}
m_localFile->close();
m_tcpClient->close();
emit finished();
}
}
void TcpUploadClient::displayError(QAbstractSocket::SocketError)
{
qDebug()<<m_tcpClient->errorString();
m_tcpClient->close();
emit progress(0, 100);
if (m_lableStatus)
{
m_lableStatus->setText("上次傳輸出現錯誤!客戶端重新就緒!");
}
}
感謝這位作者IT1995精彩的代碼,最初的代碼地址:Qt學習筆記-Qt實現文件傳輸功能(基於TCP)【客戶端傳給服務器】