Qt中使用Tcp传输文件

概述:

TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。在一个TCP连接中,仅有两方进行彼此通信。
TCP传输数据的时候是很少出现丢包情况的,因为它本身的特点,详细介绍看百度

一,传输文件的过程:

实现服务端选择文件给客户端发送:
过程如下:
在这里插入图片描述
上述过程是在建立Tcp链接之后的,要使用Tcp发送文件肯定需要先建立Tcp连接的,可以看看这个Tcp简单文本发送

二,服务端的实现

1,建立连接

    tcpServer = new QTcpServer(this);

    tcpServer->listen(QHostAddress::Any,8888);
    setWindowTitle("服务器端口为8888");
    
    /*无连接的时候按钮无效*/
    ui->file->setEnabled(false);
    ui->send->setEnabled(false);

    /*如果客户端成功和服务端连接*/
    connect(tcpServer,&QTcpServer::newConnection,[=](){
        /*取出建立好链接的套接字*/
        tcpSocket = tcpServer->nextPendingConnection();
        /*获取对方IP和端口*/
        QString ip = tcpSocket->peerAddress().toString();
        quint16 port = tcpSocket->peerPort();

        QString str = QString("[%1:%2]成功链接").arg(ip).arg(port);
        ui->textEdit->setText(str);/*显示编辑区*/
        /*恢复按钮*/
        ui->file->setEnabled(true);

    });

在连接到客户端的时候,打印出IP地址和端口号,并且显示在文本编辑区中。刚开始之前对按钮进行了一个不使能操作。也就是说,在没有连接的时候是无法点击按钮的。
2,选择文件的实现
我们需要在服务端选择文件并且发送给客户端。

   
   QString path = QFileDialog::getOpenFileName(this,"选择文件","../");
    if(path.isEmpty() == false)
    {
        fileName.clear();
        fileSize = 0;

        /*获取文件信息*/
        QFileInfo info(path);
        fileName = info.fileName();
        fileSize = info.size();
        /*发送文件的大小*/
        sendSize = 0;
        /*只读方式打开*/
        file.setFileName(path);
        if(file.open(QIODevice::ReadOnly) == true)
        {

        }else{
            qDebug() << "打开失败";
        }
        /*追加文件路径信息*/
        ui->textEdit->append(path);
        ui->file->setEnabled(false);
        ui->send->setEnabled(true);

    }else{
        qDebug() << "文件路径无效";

    }

}


在发送文件之前先发送文件的信息,比如文件名,文件大小等。
关于文件信息的获取使用Qt中的QFileInfo
3,发送文件信息

void Widget::on_send_clicked()
{
    /*先发送文件头信息*/
    QString headMessage = QString("%1##%2").arg(fileName).arg(fileSize);
    ReciveOk = false;
    quint64 len = tcpSocket->write(headMessage.toUtf8().data());
    /*发送头信息成功*/
    if(len > 0  )
    {
        /*防止Tcp连包问题,使用定时器延时20ms*/
        timer.start(20);

    }else{

        qDebug() << "头文件信息发送失败";
        file.close();

        ui->file->setEnabled(true);
        ui->send->setEnabled(false);
    }

对于文件信息的话是类似于数据的头信息,包括文件名,大小等,可以通过组包(组合成为一个字符串),在接收到这个字符串之后,再把信息分开即可。
4,发送数据
对于数据发送为了防止头信息和数据出现相互干扰的情况,需要分开发送,在发送头信息之后,延时(使用定时器)然后发送数据,确保客户端先收到头信息,再收到数据。

void Server::sendData()
{
    qint64 len = 0;
    sendsize = 0;
    do
    {
        char buf[4*1024] = {0};
        len = 0;

        len = file.read(buf, sizeof(buf));
        len = tcpSocket->write(buf, len);
        sendsize += len;

    }while(len > 0);

    /*判断数据是否发送完毕*/
    if(sendsize == fileSize)
    {
        ui->textEdit->append("文件发送完毕");
        file.close();

        /*断开客户端*/
        tcpSocket->disconnectFromHost();
    }

}

所以整个服务端代码如下:

#include "server.h"
#include "ui_server.h"

Server::Server(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Server)
{
    ui->setupUi(this);
    /*分配空间*/
    tcpServer = new QTcpServer(this);

    ui->selectFile->setEnabled(false);
    ui->sendButton->setEnabled(false);

    /*监听*/
    tcpServer->listen(QHostAddress::Any, 8888);
    /*如果客户端成功和服务器连接,套接字发出信号*/
    connect(tcpServer, &QTcpServer::newConnection, [=](){
        /*取出建立好连接的套接字*/
        tcpSocket = tcpServer->nextPendingConnection();

        /*获取对方的IP和端口*/
        QString ip = tcpSocket->peerAddress().toString();
        quint16 port = tcpSocket->peerPort();
        /*组包*/
        QString str = QString("Clinet [%1:%2] connected!").arg(ip).arg(port);
        /*显示到编辑区 或者弹出对话框*/
        ui->textEdit->setText(str);

        ui->selectFile->setEnabled(true);


    });

    connect(&timer, &QTimer::timeout, [=](){
        timer.stop();
        sendData();
    });
}

Server::~Server()
{
    delete ui;
}


void Server::on_selectFile_clicked()
{
    QString path = QFileDialog::getOpenFileName(this, "open", "../");
    if(path.isEmpty() == false)
    {
        filename.clear();
        fileSize = 0;
        /*获取文件信息*/
        QFileInfo info(path);
        filename = info.fileName();
        fileSize = info.size();

        sendsize = 0;

        file.setFileName(path);
        bool openResult = file.open(QIODevice::ReadOnly);
        if(openResult == false)
        {


        }

        ui->textEdit->append(path);
        ui->selectFile->setEnabled(false);
        ui->sendButton->setEnabled(true);

    }else
    {

        /*路径无效*/
    }

}

void Server::on_sendButton_clicked()
{
    /*先发送文件头信息*/
   QString headMessage = QString("%1##%2").arg(filename).arg(fileSize);

   qint64 sendLen = tcpSocket->write(headMessage.toUtf8());
   if(sendLen > 0)/*头部信息发送成功*/
   {
        timer.start(20);
   }else {
       qDebug() << "头部信息发送失败";
       file.close();
       ui->selectFile->setEnabled(true);
       ui->sendButton->setEnabled(false);
   }


}

void Server::sendData()
{
    qint64 len = 0;
    sendsize = 0;
    do
    {
        char buf[4*1024] = {0};
        len = 0;

        len = file.read(buf, sizeof(buf));
        len = tcpSocket->write(buf, len);
        sendsize += len;

    }while(len > 0);

    /*判断数据是否发送完毕*/
    if(sendsize == fileSize)
    {
        ui->textEdit->append("文件发送完毕");
        file.close();

        /*断开客户端*/
       // tcpSocket->disconnectFromHost();
    }

}

三,客户端的实现

客户端主要就是解析服务端发送过来的头信息以及读文件即可
代码及注释如下:

#include "client.h"
#include "ui_client.h"
#include <QMessageBox>
#include <QHostAddress>
#include <QFileDialog>
#include <QProgressBar>
#include <QDebug>

client::client(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::client)
{
    ui->setupUi(this);

    tcpSocket = new QTcpSocket(this);
    isStart = 1;

    connect(tcpSocket, &QTcpSocket::readyRead,[=](){
               /*取出接收的内容*/
        QByteArray buf = tcpSocket->readAll();

        if(isStart == 1)
        {
            isStart = 0;

            fileName = QString(buf).section("##", 0, 0);
            fileSize = QString(buf).section("##", 1, 1).toInt();
            RecvSize = 0;

            /*弹出接收文件的信息*/
            QString str = QString("接收的文件:[%1: %2KB]").arg(fileName).arg(fileSize);
            QMessageBox::information(this, "接收文件信息", str);

            /*打开文件*/
            QString path = QFileDialog::getSaveFileName(this,"save","../","souce(*.cpp)");

            if(path.isEmpty() == false)
            {
                file.setFileName(path);

                //打开文件
                if(file.open(QIODevice::WriteOnly) == true)
                {



                }
            }
            /*
            file.setFileName(fileName);
            bool isOk = file.open(QIODevice::WriteOnly);
            if(isOk == false)
            {
                qDebug() << "文件打开失败";
            }*/
        }
        if(isStart == 0)
        {
            /*文件信息*/
            qint64 len = file.write(buf.data());
            RecvSize += len;
            qDebug() << "执行到写数据的的地方";
            if(RecvSize == fileSize)
            {
                file.close();
                QMessageBox::information(this, "完成", "文件接收完成");
                tcpSocket->disconnectFromHost();
                tcpSocket->close();
            }
        }
    });
}

client::~client()
{
    delete ui;
}

void client::on_connectButton_clicked()
{
    /*获取IP和端口*/
    QString ip = ui->lineEditIP->text();
    quint64 port = ui->lineEditPort->text().toInt();

    tcpSocket->connectToHost(QHostAddress(ip),port);

}

void client::recvFile()
{
    QString path = QFileDialog::getSaveFileName(this,"save","../","souce(*.cpp)");

    if(path.isEmpty() == false)
    {
        file.setFileName(path);

        //打开文件
        if(file.open(QIODevice::WriteOnly) == true)
        {



        }
    }

}

对于客户端来说,主要就是获取发送的头信息,不能和数据相互连包,其余和之前的Tcp数据传输一样。

  • 8
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值