玩命加载中 . . .

Qt下tcp实现文件传输


简单实现文件传输。

程序代码如下:

一、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();
}

<4>界面文件

server.ui

三、相关截图

<1>运行结果截图如下:


<2>程序代码截图


  目录