简单实现文件传输。
程序代码如下:
一、TcpClient
<1>TcpClient.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-12-02T16:29:38
#
#-------------------------------------------------
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = TcpClient
TEMPLATE = app
SOURCES += main.cpp\
client.cpp
HEADERS += client.h
FORMS += client.ui
<2>头文件
client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <QWidget>
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
#include <QHostAddress>
#include <QFileDialog>
#include <QFile>
namespace Ui {
class client;
}
class client : public QWidget
{
Q_OBJECT
public:
explicit client(QWidget *parent = 0);
~client();
private slots:
void on_Connect_clicked();
void doProcessConnected();
void on_Exit_clicked();
void on_UploadFiles_clicked();
void on_SelectFiles_clicked();
void on_DisConnect_clicked();
void send(); //连接服务器
void startTransfer(); //发送文件大小等信息
void displayError(QAbstractSocket::SocketError); //显示错误
void updateClientProgress(qint64); //发送数据,更新进度条
private:
Ui::client *ui;
QTcpServer *tcpServer; //监听套接字
QTcpSocket *myClient; //通信套接字
QFile *myFile; //文件对象
QString filePath;//保存文件路径
qint64 totalSize; //文件大小
qint64 sendSize; //每次发送数据的大小
qint64 alreadySend; //已经发送数据大小
qint64 residueSize; //剩余数据大小
QString fileName; //文件名字
QString ip; //服务器ip
QString servPort; //服务器端口
QByteArray outBlock; //数据缓冲区,即存放每次要发送的数据
void Init();
};
#endif // CLIENT_H
<3>源文件
3-1 client.cpp
#include "client.h"
#include "ui_client.h"
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QFileInfo>
client::client(QWidget *parent) :
QWidget(parent),
ui(new Ui::client)
{
ui->setupUi(this);
Init();
setWindowTitle("客户端");
//当有数据发送成功时,更新进度条
connect(myClient,SIGNAL(bytesWritten(qint64)),this,
SLOT(updateClientProgress(qint64)));
connect(myClient,SIGNAL(error(QAbstractSocket::SocketError)),this,
SLOT(displayError(QAbstractSocket::SocketError)));
}
client::~client()
{
delete ui;
}
//初始化
void client::Init()
{
totalSize = 0;
sendSize = 4*1024;
alreadySend = 0;
residueSize = 0;
myClient = new QTcpSocket(this);
//未连接,"断开"按钮不可用
ui->DisConnect->setEnabled(false);
}
//连接
void client::on_Connect_clicked()
{
ip = ui->lineEdit->text();
if(ip.isEmpty())
{
QMessageBox::critical(this,tr("错误信息"),"请填写IP地址!");
return;
}
servPort = ui->lineEdit_2->text();
if(servPort.isEmpty())
{
QMessageBox::critical(this,tr("错误信息"),"请填写端口号!");
return;
}
myClient->connectToHost(QHostAddress(ip),servPort.toUInt());
//ui->displayInfo->setText(tr("连接中..."));
//myClient->connectToHost(ui->lineEdit->text(),ui->lineEdit_2->text().toInt());//连接
connect(myClient,SIGNAL(connected()),
this,SLOT(doProcessConnected()));
ui->SelectFiles->setEnabled(true);//“选择文件”按钮可用
ui->UploadFiles->setEnabled(false);//“发送文件”按钮不可用
}
//提示成功连接
void client::doProcessConnected()
{
QString msg = QString("与[%1:%2] 成功连接").arg(ip).arg(servPort);
ui->textEdit->append(msg);
//成功连接后,"连接"按钮不可用,"断开"按钮可用
ui->Connect->setEnabled(false);
ui->DisConnect->setEnabled(true);
}
//选择文件
void client::on_SelectFiles_clicked()
{
filePath = QFileDialog::getOpenFileName(this);
if(!filePath.isEmpty())//如果选择文件路径有效
{
QString msg;
//提示打开文件的路径
msg = QString("文件来自于【%1】!").arg(filePath);
ui->textEdit->append(msg);
//获取文件信息
QFileInfo info(filePath);
fileName = info.fileName(); //获取文件名字
msg = QString("打开文件【%1】成功!").arg(fileName);
ui->textEdit->append(msg);
//选择文件后,“选择文件”按钮不可用,“发送文件”按钮可用
ui->SelectFiles->setEnabled(false);
ui->UploadFiles->setEnabled(true);
}
else
{
qDebug() << "选择文件路径出错 ";
}
}
//执行发送
void client::send()
{
//当连接服务器成功,开始传送文件
startTransfer();
}
//发送文件
void client::on_UploadFiles_clicked()
{
send();
/*
myClient->connectToHost(QHostAddress(ip),servPort.toUInt());
*/
ui->UploadFiles->setEnabled(false);
alreadySend = 0;//初始化已发送字节为0
}
//实现文件大小等信息的发送
void client::startTransfer()
{
myFile = new QFile(filePath);
if(!myFile->open(QFile::ReadOnly))
{
qDebug() << "文件打开错误!";
return;
}
//文件总大小
totalSize = myFile->size();
QDataStream sendOut(&outBlock,QIODevice::WriteOnly);
QString currentFileName = filePath.right(filePath.size() - filePath.lastIndexOf('/')-1);
//依次写入总大小信息空间,文件名大小信息空间,文件名
sendOut << qint64(0) << qint64(0) << currentFileName;
//这里的总大小是文件名大小等信息和实际文件大小的总和
totalSize += outBlock.size();
sendOut.device()->seek(0);
//返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间
sendOut<<totalSize<<qint64((outBlock.size() - sizeof(qint64)*2));
//发送完头数据后剩余数据的大小
residueSize = totalSize - myClient->write(outBlock);
//清空发送缓冲区
outBlock.resize(0);
}
//更新进度条,实现文件的传送
void client::updateClientProgress(qint64 numBytes)
{
//已经发送数据的大小
alreadySend += (int)numBytes;
if(residueSize > 0) //如果已经发送了数据
{
//每次发送sendSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB,
//就发送剩余数据的大小
outBlock = myFile->read(qMin(residueSize,sendSize));
//发送完一次数据后还剩余数据的大小
residueSize -= (int)myClient->write(outBlock);
//清空发送缓冲区
outBlock.resize(0);
} else {
myFile->close(); //如果没有发送任何数据,则关闭文件
}
//更新进度条
ui->progressBar->setMaximum(totalSize);
ui->progressBar->setValue(alreadySend);
ui->displayInfo->setText(tr("传送文件中..."));
if(alreadySend == totalSize) //发送完毕
{
ui->displayInfo->clear();
myFile->close();
//发送文件后,“选择文件”按钮可用
ui->SelectFiles->setEnabled(true);
//重置进度条
ui->progressBar->reset();
QString msg = QString("传送文件【 %1 】成功").arg(fileName);
ui->textEdit->append(msg);
/*
myClient->disconnectFromHost();
myClient->close();
*/
}
}
//显示错误
void client::displayError(QAbstractSocket::SocketError)
{
qDebug() << myClient->errorString();
myClient->disconnectFromHost();
myClient->close();
ui->progressBar->reset();
ui->displayInfo->clear();
ui->textEdit->append("---服务器端并未绑定该IP或端口有误---");
ui->UploadFiles->setEnabled(true);
}
//退出
void client::on_Exit_clicked()
{
myClient->abort();
if(myFile->isOpen())
{
myFile->close();
}
close();
}
//断开
void client::on_DisConnect_clicked()
{
myClient->disconnectFromHost();
myClient->close();
QString msg = "与服务器断开连接!";
ui->textEdit->append(msg);
ui->Connect->setEnabled(true);
ui->DisConnect->setEnabled(false);
}
3-2 main.cpp
#include "client.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
client w;
w.show();
return a.exec();
}
<4>界面文件
client.ui
二、TcpServer
<1>TcpServer.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-12-02T16:30:32
#
#-------------------------------------------------
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = TcpServer
TEMPLATE = app
SOURCES += main.cpp\
server.cpp
HEADERS += server.h
FORMS += server.ui
<2>头文件
server.h
#ifndef SERVER_H
#define SERVER_H
#include <QWidget>
#include <QTcpServer> //监听套接字
#include <QTcpSocket> //通信套接字
#include <QFile>
#include <QMessageBox>
namespace Ui {
class server;
}
class server : public QWidget
{
Q_OBJECT
public:
explicit server(QWidget *parent = 0);
~server();
private slots:
void on_pushButton_clicked();
void doProcessNewConnection();
void doProcessReadyRead();
void doProcessDisconnected();
void on_Exit_clicked();
void doProcessAccepptError(QAbstractSocket::SocketError);
private:
Ui::server *ui;
QTcpServer *myServer;
QTcpSocket *client;
qint64 recvTotal; //接收的文件实际数据
qint64 allTotal; //文件的实际大小
qint64 fileNameSize; //文件名的大小信息
QString fileName; //存放文件名
QByteArray inBlock; //数据缓冲区
QFile *myFile;
void Init();
};
#endif // SERVER_H
<3>源文件
3-1 main.cpp
#include "server.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
server w;
w.show();
return a.exec();
}
3-2 server.cpp
#include "server.h"
#include "ui_server.h"
#include <QDebug>
#include <QMessageBox>
#define MAXNUM 100
server::server(QWidget *parent) :
QWidget(parent),
ui(new Ui::server)
{
ui->setupUi(this);
Init();
setWindowTitle("服务器端");
}
server::~server()
{
delete ui;
}
void server::Init()
{
recvTotal = 0;
allTotal = 0;
fileNameSize = 0;
myServer = new QTcpServer(this);
}
void server::on_pushButton_clicked()
{
//手动设置IP
QString myAddr = ui->lineEdit->text();
if(myAddr.isEmpty())
{
QMessageBox::critical(this,tr("错误信息"),"请填写IP地址!");
return;
}
QString myPort = ui->lineEdit_2->text();
if(myPort.isEmpty())
{
QMessageBox::critical(this,tr("错误信息"),"请填写端口号!");
return;
}
QString msg;
bool ret = myServer->listen(QHostAddress(myAddr),myPort.toInt());
if(!ret)
{
msg = "绑定失败,输入的IP地址有误或输入的IP地址不是本机的IP地址!";
}
else
{
msg = "绑定成功!";
ui->pushButton->setEnabled(false);
}
ui->textEdit->append(msg);
myServer->setMaxPendingConnections(MAXNUM);
connect(myServer,SIGNAL(newConnection()),
this,SLOT(doProcessNewConnection()));
connect(myServer,SIGNAL(acceptError(QAbstractSocket::SocketError)),
this,SLOT(doProcessAccepptError(QAbstractSocket::SocketError)));
}
void server::doProcessNewConnection()
{
client = myServer->nextPendingConnection();
QString msg = QString("客户端[%1:%2] 连入!")
.arg(client->peerAddress().toString())
.arg(client->peerPort());
ui->textEdit->append(msg);
//客户端断开
connect(client,SIGNAL(disconnected()),
this,SLOT(doProcessDisconnected()));
//读取内容
connect(client,SIGNAL(readyRead()),
this,SLOT(doProcessReadyRead()));
}
void server::doProcessDisconnected()
{
client = (QTcpSocket *)this->sender();
QString msg = QString("客户端[%1:%2] 退出!")
.arg(client->peerAddress().toString())
.arg(client->peerPort());
ui->textEdit->append(msg);
}
void server::doProcessReadyRead()
{
QDataStream in(client);
if(recvTotal <= sizeof(qint64)*2)
{ //如果接收到的数据小于16个字节,那么是刚开始接收数据,保存到//来的头文件信息
if((client->bytesAvailable() >= sizeof(qint64)*2)
&& (fileNameSize == 0))
{ //接收数据总大小信息和文件名大小信息
in >> allTotal >> fileNameSize;
recvTotal += sizeof(qint64) * 2;
}
if((client->bytesAvailable() >= fileNameSize)
&& (fileNameSize != 0))
{ //接收文件名,并建立文件
in >> fileName;
QString msg = QString("接收文件【 %1 】...").arg(fileName);
ui->textEdit->append(msg);
recvTotal += fileNameSize;
myFile = new QFile(fileName);
if(!myFile->open(QFile::WriteOnly))
{
qDebug() << "文件打开错误!";
return;
}
}
else return;
}
if(recvTotal < allTotal)
{ //如果接收的数据小于总数据,那么写入文件
recvTotal += client->bytesAvailable();
inBlock = client->readAll();
myFile->write(inBlock);
inBlock.resize(0);
}
//更新进度条
ui->progressBar->setMaximum(allTotal);
ui->progressBar->setValue(recvTotal);
ui->displayInfo->setText(tr("接收文件中..."));
if(recvTotal == allTotal)
{ //接收数据完成时
myFile->close();
recvTotal = 0;
allTotal = 0;
fileNameSize = 0;
ui->progressBar->reset();
ui->displayInfo->clear();
//ui->pushButton->setEnabled(true);
QString msg = QString("接收文件【 %1 】成功!").arg(fileName);
ui->textEdit->append(msg);
ui->textEdit->append("---文件保存在生成的debug文件夹下---");
}
}
void server::doProcessAccepptError(QAbstractSocket::SocketError)
{
qDebug() << client->errorString();
client->close();
ui->progressBar->reset();
ui->textEdit->append("服务端就绪");
ui->pushButton->setEnabled(true);
}
//退出
void server::on_Exit_clicked()
{
close();
}