最近有个项目要用到TCP文件传输,要求服务端和客户端均能发送文件和接收文件,网上几乎都是单向传文件,所以自己写,分为服务端和客户端。
服务端部分代码:
#include "tcptransmitserver.h"
TCPTransmitServer::TCPTransmitServer(QObject *parent)
: QTcpServer(parent)
{
}
TCPTransmitServer::~TCPTransmitServer()
{
}
bool TCPTransmitServer::startServer(int port)
{
#if(QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
return listen(QHostAddress::AnyIPv4, port);
#else
return listen(QHostAddress::Any, port);
#endif
}
void TCPTransmitServer::stopServer()
{
close();
}
void TCPTransmitServer::incomingConnection(qintptr socketDescriptor)
{
transmitFile = new TransmitFileThread(socketDescriptor, this);
QString ip = transmitFile->tcpSocket->peerAddress().toString();
quint16 port = transmitFile->tcpSocket->peerPort();
emit connectedSignal(ip, port);
connect(transmitFile, &TransmitFileThread::receiveMessageSignal, this, &TCPTransmitServer::receiveMessageSignal);
connect(transmitFile, &TransmitFileThread::receiveFileNameSignal, this, &TCPTransmitServer::receiveFileNameSignal);
connect(transmitFile, &TransmitFileThread::receiveFileSizeSignal, this, &TCPTransmitServer::receiveFileSizeSignal);
connect(transmitFile, &TransmitFileThread::receiveDataSignal, this, &TCPTransmitServer::receiveDataSignal);
connect(transmitFile, &TransmitFileThread::receiveFinishedSignal, this, &TCPTransmitServer::receiveFinishedSignal);
connect(transmitFile, &QThread::finished, this, &TCPTransmitServer::receiveFinishedSignal);
connect(transmitFile, &TransmitFileThread::sendFileSizeSignal, this, &TCPTransmitServer::sendFileSizeSignal);
connect(transmitFile->tcpSocket, &QIODevice::bytesWritten, this, &TCPTransmitServer::bytesWrittenSignal);
connect(transmitFile, &TransmitFileThread::sendMessageSignal, this, &TCPTransmitServer::sendMessageSignal);
connect(transmitFile, &TransmitFileThread::sendFinishedSignal, this, &TCPTransmitServer::sendFinishedSignal);
connect(transmitFile, &QThread::finished, transmitFile, &QObject::deleteLater);
transmitFile->start();
}
void TCPTransmitServer::SendFile(QString fileName)
{
transmitFile->SendFile(fileName);
}
#ifndef TCPTRANSMITSERVER_H
#define TCPTRANSMITSERVER_H
#include <QtCore/qglobal.h>
#if QT_VERSION >= 0x050000
#include <QtWidgets/QWidget>
#else
#include <QtGui/QWidget>
#endif
#include <QTcpServer>
#include "transmitFileThread.h"
class TCPTransmitServer : public QTcpServer
{
Q_OBJECT
public:
explicit TCPTransmitServer(QObject *parent = nullptr);
~TCPTransmitServer();
bool startServer(int port);
void stopServer();
void SendFile(QString fileName);
signals:
void connectedSignal(QString ip, quint16 port);
void receiveFileNameSignal(QString name);
void receiveFileSizeSignal(qint64 size);
void receiveFinishedSignal();
void receiveMessageSignal(QString msg);
void receiveDataSignal(qint64 size);
void sendFileSizeSignal(qint64 size);
void sendMessageSignal(QString msg);
void sendFinishedSignal();
void bytesWrittenSignal(qint64 size);
protected:
virtual void incomingConnection(qintptr socketDescriptor);
private:
TransmitFileThread *transmitFile;
};
#endif // TCPTRANSMITSERVER_H
#include "transmitFileThread.h"
TransmitFileThread::TransmitFileThread(int socketDescriptor, QObject *parent)
: QThread(parent)
{
blockSize = 0;
blockNumber = 0;
tcpSocket = new QTcpSocket(this);
tcpSocket->setSocketDescriptor(socketDescriptor);
connect(tcpSocket, &QIODevice::readyRead, this, &TransmitFileThread::ReceiveData);
//connect(tcpSocket, &QAbstractSocket::disconnected, this, &TransmitFileThread::DisConnect);
connect(tcpSocket, QOverload< QAbstractSocket::SocketError >::of(&QAbstractSocket::error), this, &TransmitFileThread::displaySocketError);
}
TransmitFileThread::~TransmitFileThread()
{
}
void TransmitFileThread::run()
{
emit receiveMessageSignal(tr("收到新连接请求:[%1:%2]")
.arg(tcpSocket->peerAddress().toString())
.arg(tcpSocket->peerPort()));
exec();
}
void TransmitFileThread::DisConnect()
{
emit receiveMessageSignal(tr("断开连接"));
deleteLater();
}
void TransmitFileThread::SendFile(QString fileName)
{
emit sendMessageSignal(tr("连接成功connect succesful"));
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly))
{
emit sendMessageSignal(tr("文件不能打开进行读取file don't open"));
return;
}
else
{
emit sendFileSizeSignal(file.size());
}
qint64 size;
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_DefaultCompiledVersion);
QFileInfo fileInfo(fileName);
QString name = fileInfo.fileName(); //获取文件名,含扩展名
//写入开始符及文件名称
emit sendMessageSignal(tr("发送开始符及文件名称send start signal and file name"));
block.clear();
out.device()->seek(0);
out << 0x01 << name.toUtf8();
size = block.size();
tcpSocket->write((char *)&size, sizeof(qint64));
tcpSocket->write(block.data(), size);
if(!tcpSocket->waitForBytesWritten(-1))
{
emit sendMessageSignal(tr("发送开始符数据发生错误:%1").arg(tcpSocket->errorString()));
return;
}
//写入文件大小
emit sendMessageSignal(tr("发送文件大小file size:%1").arg(file.size()));
block.clear();
out.device()->seek(0);
out << 0x02 << QString::number(file.size()).toUtf8();
size = block.size();
tcpSocket->write((char *)&size, sizeof(qint64));
tcpSocket->write(block.data(), size);
if(!tcpSocket->waitForBytesWritten(-1))
{
emit sendMessageSignal(tr("发送文件大小数据发生错误:%1").arg(tcpSocket->errorString()));
return;
}
//循环写入文件数据
do
{
block.clear();
out.device()->seek(0);
//每次最多读取0xFFFF 即65535个字节发送,对于大文件如果一次性读取完内存不一定吃得消
//每次发送的文件数据都带上一个0x03标识符
out << 0x03 << file.read(0xFFFF);
size = block.size();
emit sendMessageSignal(tr("当前发送数据大小current send size:%1字节").arg(size));
tcpSocket->write((char *)&size, sizeof(qint64));
tcpSocket->write(block.data(), size);
if(!tcpSocket->waitForBytesWritten(-1))
{
emit sendMessageSignal(tr("发送文件数据发生错误:%1").arg(tcpSocket->errorString()));
return;
}
} while(!file.atEnd());
//写入结束符及文件名称
emit sendMessageSignal(tr("发送结束符及文件名称send end signal and filename"));
block.clear();
out.device()->seek(0);
out << 0x04 << name.toUtf8();
size = block.size();
tcpSocket->write((char *)&size, sizeof(qint64));
tcpSocket->write(block.data(), size);
if(!tcpSocket->waitForBytesWritten(-1))
{
emit sendMessageSignal(tr("发送结束符数据发生错误:%1").arg(tcpSocket->errorString()));
return;
}
file.close();
emit sendFinishedSignal();
emit sendMessageSignal(tr("发送文件完毕send finish"));
}
void TransmitFileThread::ReceiveData()
{
while(tcpSocket->bytesAvailable() >= sizeof(quint64))
{
if(blockSize == 0)
{
if(tcpSocket->bytesAvailable() < sizeof(qint64))
{
return;
}
tcpSocket->read((char *)&blockSize, sizeof(qint64));
}
if(tcpSocket->bytesAvailable() < blockSize)
{
return;
}
emit receiveDataSignal(blockSize + sizeof(qint64));
QByteArray data = tcpSocket->read(blockSize);
proccessData(data);
blockSize = 0;
}
}
void TransmitFileThread::proccessData(QByteArray &array)
{
QDataStream in(&array, QIODevice::ReadOnly);
in.setVersion(QDataStream::Qt_DefaultCompiledVersion);
int key;
QByteArray data;
in >> key >> data;
blockNumber++;
emit receiveMessageSignal(tr("已接收数据包:%1个").arg(blockNumber));
emit receiveMessageSignal(tr("收到标识符:%1 当前数据包大小:%2字节").arg(key).arg(data.size()));
switch(key)
{
case 0x01:
fileName = fileName.fromUtf8(data.data(), data.size());
file.setFileName(qApp->applicationDirPath() + "/" + fileName);
emit receiveFileNameSignal(file.fileName());
if(file.exists())
{
file.remove();
}
if(!file.open(QIODevice::WriteOnly))
{
emit receiveMessageSignal(tr("不能打开文件进行写入"));
break;
}
break;
case 0x02:
{
QString size = QString::fromUtf8(data.data(), data.size());
emit receiveFileSizeSignal(size.toUInt());
break;
}
case 0x03:
file.write(data.data(), data.size());
file.flush();
break;
case 0x04:
file.close();
blockSize = 0;
blockNumber = 0;
emit receiveFinishedSignal();
emit receiveMessageSignal(tr("接收文件完毕receive finish"));
break;
}
}
void TransmitFileThread::displaySocketError(QAbstractSocket::SocketError)
{
emit receiveMessageSignal(tr("Socket错误:%1[%2]").arg(tcpSocket->errorString()));
//QMessageBox::information(0, tr("提示"), tr("Socket错误:") + tcpSocket->errorString());
if(file.isOpen())
{
emit receiveMessageSignal(tr("正在移除文件:%1").arg(file.fileName()));
file.close();
}
else
{
return;
}
if(!file.fileName().isEmpty())
{
emit receiveMessageSignal(tr("正在移除文件:%1").arg(fileName));
file.remove(fileName);
}
else
{
return;
}
}
客户端部分代码:
#include "frmclient.h"
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QDirModel>
#include <QItemSelectionModel>
#include "ui_frmclient.h"
frmClient::frmClient(QWidget *parent)
: QDialog(parent),
ui(new Ui::frmClient)
{
ui->setupUi(this);
this->setWindowTitle(ui->lab_Title->text());
this->InitForm();
client = new TCPTransmitClient(this);
connect(client, &QTcpSocket::connected, this, &frmClient::updateConnectStatus);
connect(client, &TCPTransmitClient::receiveFinishedSignal, this, &frmClient::transmitFinshed);
connect(client, &TCPTransmitClient::receiveMessageSignal, this, &frmClient::updateTransmitStatus);
connect(client, &TCPTransmitClient::receiveFileNameSignal, this, &frmClient::updateReceiveFileName);
connect(client, &TCPTransmitClient::receiveFileSizeSignal, this, &frmClient::setTransmitProgress);
connect(client, &TCPTransmitClient::receiveDataSignal, this, &frmClient::updateTransmitProgress);
connect(client, &TCPTransmitClient::sendFileSizeSignal, this, &frmClient::setTransmitProgress);
connect(client, &TCPTransmitClient::bytesWritten, this, &frmClient::updateTransmitProgress);
connect(client, &TCPTransmitClient::sendMessageSignal, this, &frmClient::updateTransmitStatus);
connect(client, &TCPTransmitClient::sendFinishedSignal, this, &frmClient::transmitFinshed);
connect(client, QOverload< QAbstractSocket::SocketError >::of(&QAbstractSocket::error), this, &frmClient::socketError);
connect(ui->btnExit, &QPushButton::clicked, this, &frmClient::close);
}
frmClient::~frmClient()
{
delete ui;
client->deleteLater();
}
void frmClient::InitForm()
{
transmitBytes = 0;
transmitBlockNumber = 0;
transmitMaxBytes = 0;
ui->txtSendFile->setText(tr("请选择文件"));
ui->txtReceiveFile->setText(tr("等待接收文件"));
ui->txtTransmitStatus->setText((tr("等待连接")));
ui->txtTransmitStatus_1->setText((tr("尚未连接")));
ui->pbTransmitProgress->setRange(0, 100);
ui->pbTransmitProgress->setValue(0);
ui->btnSelect->setEnabled(false);
ui->btnSend->setEnabled(false);
}
void frmClient::on_btnSelect_clicked()
{
QString filePath = QFileDialog::getOpenFileName(this, tr("选择文件"), QCoreApplication::applicationDirPath(), tr("所有文件 (*.*)"));
file.setFileName(filePath);
ui->txtSendFile->setText(file.fileName());
ui->pbTransmitProgress->setRange(0, 100);
ui->pbTransmitProgress->setValue(0);
ui->btnSend->setEnabled(true);
}
void frmClient::on_btnSend_clicked()
{
transmitBytes = 0;
transmitBlockNumber = 0;
transmitMaxBytes = 0;
client->SendFile(file.fileName());
}
void frmClient::updateTransmitProgress(qint64 size)
{
transmitBlockNumber++;
transmitBytes += size;
ui->pbTransmitProgress->setValue(transmitBytes);
QString msg = tr("已传输数据包:%1个 当前数据包大小:%2字节 已传输字节:%3 总共字节:%4")
.arg(transmitBlockNumber)
.arg(size)
.arg(transmitBytes)
.arg(transmitMaxBytes);
ui->txtTransmitStatus->setText(msg);
qApp->processEvents(); //及时刷新界面
}
void frmClient::updateTransmitStatus(QString msg)
{
qDebug() << tr("客户端Client:") << msg;
ui->txtTransmitStatus_1->setText(msg);
}
void frmClient::setTransmitProgress(qint64 size)
{
transmitBytes = 0;
transmitBlockNumber = 0;
transmitMaxBytes = size;
ui->pbTransmitProgress->setRange(0, size - 1);
ui->pbTransmitProgress->setValue(0);
}
void frmClient::transmitFinshed()
{
ui->pbTransmitProgress->setRange(0, 100);
ui->pbTransmitProgress->setValue(100);
ui->txtTransmitStatus_1->setText(tr("文件传输完成"));
}
void frmClient::on_btnConnect_clicked()
{
client->connectToHost(ui->txtServerIP->text(), ui->txtServerPort->text().toInt()); //App::ServerIP, App::ServerPort
ui->btnConnect->setEnabled(false);
ui->btnDisconnect->setEnabled(true);
ui->pbTransmitProgress->setValue(0);
ui->btnSelect->setEnabled(true);
}
void frmClient::on_btnDisconnect_clicked()
{
client->disconnectFromHost();
client->close();
ui->txtTransmitStatus_1->setText(tr("Socket:%1[%2]").arg(client->errorString()));
if(file.isOpen())
{
file.close();
}
if(!file.fileName().isEmpty())
{
file.remove(file.fileName());
}
ui->btnConnect->setEnabled(true);
ui->btnDisconnect->setEnabled(false);
ui->btnSelect->setEnabled(false);
ui->btnSend->setEnabled(false);
}
void frmClient::updateReceiveFileName(QString name)
{
ui->pbTransmitProgress->setRange(0, 100);
ui->pbTransmitProgress->setValue(100);
ui->txtReceiveFile->setText(name);
}
void frmClient::updateConnectStatus()
{
ui->txtTransmitStatus->setText(tr("与服务端[%1:%2]成功连接").arg(ui->txtServerIP->text()).arg(ui->txtServerPort->text().toInt()));
ui->txtTransmitStatus_1->setText(tr("成功连接到服务端[%1:%2]").arg(ui->txtServerIP->text()).arg(ui->txtServerPort->text().toInt()));
}
void frmClient::socketError(QAbstractSocket::SocketError)
{
ui->btnConnect->setEnabled(true);
ui->btnDisconnect->setEnabled(false);
updateTransmitStatus(tr("发生错误:%1").arg(client->errorString()));
//QMessageBox::information(this, tr("提示"), tr("Socket错误:") + client->errorString());
}
#include "tcptransmitclient.h"
TCPTransmitClient::TCPTransmitClient(QObject *parent)
: QTcpSocket(parent)
{
blockSize = 0;
blockNumber = 0;
connect(this, &QIODevice::readyRead, this, &TCPTransmitClient::ReceiveData);
//connect(this, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TCPTransmitClient::displaySocketError);
}
TCPTransmitClient::~TCPTransmitClient()
{
}
void TCPTransmitClient::SendFile(QString fileName)
{
emit sendMessageSignal("连接成功connect succesful");
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly))
{
emit sendMessageSignal("文件不能打开进行读取file don't open");
return;
}
else
{
emit sendFileSizeSignal(file.size());
}
qint64 size;
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_DefaultCompiledVersion);
QFileInfo fileInfo(fileName);
QString name = fileInfo.fileName(); //获取文件名,含扩展名
//写入开始符及文件名称
emit sendMessageSignal("发送开始符及文件名称send start signal and file name");
block.clear();
out.device()->seek(0);
out << 0x01 << name.toUtf8();
size = block.size();
write((char *)&size, sizeof(qint64));
write(block.data(), size);
if(!waitForBytesWritten(-1))
{
emit sendMessageSignal(QString("发送开始符数据发生错误:%1").arg(errorString()));
return;
}
//写入文件大小
emit sendMessageSignal(QString("发送文件大小file size:%1").arg(file.size()));
block.clear();
out.device()->seek(0);
out << 0x02 << QString::number(file.size()).toUtf8();
size = block.size();
write((char *)&size, sizeof(qint64));
write(block.data(), size);
if(!waitForBytesWritten(-1))
{
emit sendMessageSignal(QString("发送文件大小数据发生错误:%1").arg(errorString()));
return;
}
//循环写入文件数据
do
{
block.clear();
out.device()->seek(0);
//每次最多读取0xFFFF 即65535个字节发送,对于大文件如果一次性读取完内存不一定吃得消
//每次发送的文件数据都带上一个0x03标识符
out << 0x03 << file.read(0xFFFF);
size = block.size();
emit sendMessageSignal(QString("当前发送数据大小current send size:%1字节").arg(size));
write((char *)&size, sizeof(qint64));
write(block.data(), size);
if(!waitForBytesWritten(-1))
{
emit sendMessageSignal(QString("发送文件数据发生错误:%1").arg(errorString()));
return;
}
} while(!file.atEnd());
//写入结束符及文件名称
emit sendMessageSignal("发送结束符及文件名称send end signal and filename");
block.clear();
out.device()->seek(0);
out << 0x04 << name.toUtf8();
size = block.size();
write((char *)&size, sizeof(qint64));
write(block.data(), size);
if(!waitForBytesWritten(-1))
{
emit sendMessageSignal(QString("发送结束符数据发生错误:%1").arg(errorString()));
return;
}
file.close();
emit sendFinishedSignal();
emit sendMessageSignal("发送文件完毕send finish");
}
void TCPTransmitClient::ReceiveData()
{
while(bytesAvailable() >= sizeof(quint64))
{
if(blockSize == 0)
{
if(bytesAvailable() < sizeof(qint64))
{
return;
}
read((char *)&blockSize, sizeof(qint64));
}
if(bytesAvailable() < blockSize)
{
return;
}
emit receiveDataSignal(blockSize + sizeof(qint64));
QByteArray data = read(blockSize);
proccessData(data);
blockSize = 0;
}
}
void TCPTransmitClient::proccessData(QByteArray &array)
{
QDataStream in(&array, QIODevice::ReadOnly);
in.setVersion(QDataStream::Qt_DefaultCompiledVersion);
int key;
QByteArray data;
in >> key >> data;
blockNumber++;
emit receiveMessageSignal(tr("已接收数据包:%1个").arg(blockNumber));
emit receiveMessageSignal(tr("收到标识符:%1 当前数据包大小:%2字节").arg(key).arg(data.size()));
switch(key)
{
case 0x01:
fileName = fileName.fromUtf8(data.data(), data.size());
file.setFileName(qApp->applicationDirPath() + "/" + fileName);
emit receiveFileNameSignal(file.fileName());
if(file.exists())
{
file.remove();
}
if(!file.open(QIODevice::WriteOnly))
{
emit receiveMessageSignal(tr("不能打开文件进行写入"));
break;
}
break;
case 0x02:
{
QString size = QString::fromUtf8(data.data(), data.size());
emit receiveFileSizeSignal(size.toUInt());
break;
}
case 0x03:
file.write(data.data(), data.size());
file.flush();
break;
case 0x04:
file.close();
blockSize = 0;
blockNumber = 0;
emit receiveFinishedSignal();
emit receiveMessageSignal(tr("接收文件完毕receive finish"));
//tcpSocket->disconnectFromHost();
break;
}
}
void TCPTransmitClient::displaySocketError(QAbstractSocket::SocketError)
{
emit sendMessageSignal(tr("Socket错误:%1").arg(errorString()));
QMessageBox::information(0, tr("提示"), tr("Socket error:") + this->errorString());
}
如果你看完这篇博文,觉得对你有帮助,并且愿意赞助,那么我会更有动力写下去。