当前位置:   article > 正文

Qt之TCP传输文件_qt tcp文件传输

qt tcp文件传输

概述:

功能:
TCP(Transmission Control Protocol传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。
连接建立:
TCP的三次握手
TCP的三次握手
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接,TCP使用的流量控制协议是可变大小的滑动窗口协议。
TCP三次握手的过程如下:
客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
可靠性:
TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP。

代码示例:

.pro

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = TCPFile
TEMPLATE = app


SOURCES += main.cpp\
        serverwidget.cpp \
    clientwidget.cpp

HEADERS  += serverwidget.h \
    clientwidget.h

FORMS    += serverwidget.ui \
    clientwidget.ui

CONFIG += C++11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

client.h

#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H

#include <QWidget>
#include <QtNetwork>
#include <QFile>

namespace Ui {
class ClientWidget;
}

class ClientWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ClientWidget(QWidget *parent = 0);
    ~ClientWidget();

private slots:
    /**
     * @brief ifConnected   连接成功槽函数
     */
    void slot_Connected();

    /**
     * @brief slotReadyRead 数据接收槽函数
     */
    void slot_ReadyRead();

private slots:
    void on_buttonConnect_clicked();//客户端链接按钮

private:
    Ui::ClientWidget *ui;

    QTcpSocket *tcpSocket;  //通信套接字
    QFile file;             //文件对象
    QString fileName;       //文件名字
    qint64 fileSize;        //文件大小
    qint64 recvSize;        //已接收文件的大小
    bool isFile;            //接收文件数据标志
};

#endif // CLIENTWIDGET_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

client.cpp

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QMessageBox>
#include <QDebug>

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

    //创建TCP套接字
    tcpSocket = new QTcpSocket(this);
    //进度条初始化
    ui->progressBar->setValue(0);
    //设置标题
    setWindowTitle("客户端");

    //连接成功
    connect(tcpSocket,&QTcpSocket::connected,this,&ClientWidget::slot_Connected);
    //数据接收
    connect(tcpSocket,&QTcpSocket::readyRead,this,&ClientWidget::slot_ReadyRead);
}

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

void ClientWidget::slot_Connected()
{
    //初始化数据
    fileName = "";
    fileSize = 0;
    recvSize = 0;
    isFile = false;

    ui->buttonConnect->setEnabled(false);
    ui->textEdit->clear();
    ui->textEdit->append("和服务器连接成功");
}

void ClientWidget::slot_ReadyRead()
{
    QByteArray buf = tcpSocket->readAll();
    //buf.resize(tcpSocket->bytesAvailable());

    if(false == isFile)//先接收头部信息
    {
        isFile = true;

        //文件名字
        fileName = QString(buf).section("#", 1, 1);
        //文件大小
        fileSize = QString(buf).section("#", 2, 2).toInt();
        qDebug() << fileName << fileSize;

        recvSize = 0;

        file.setFileName(fileName);
        if(false == file.open(QIODevice::WriteOnly)){
            //初始化数据
            fileName = "";
            fileSize = 0;
            recvSize = 0;
            isFile = false;

            QMessageBox::warning(this, "警告", "创建文件失败");
            return;
        }

        ui->progressBar->setMinimum(0);
        ui->progressBar->setMaximum(fileSize/1024);

        ui->textEdit->append(QString("正在接收文件:\n%1").arg(fileName));

    }
    else//文件数据
    {
        //写入数据
        qint64 len = file.write(buf);
        recvSize += len;
        qDebug() << len;
        ui->progressBar->setValue(recvSize/1024);

    }

    if(recvSize == fileSize)//如果接收数据长度和发送数据长度相等做接收后处理
    {
        file.close();
        ui->buttonConnect->setEnabled(true);
        tcpSocket->disconnectFromHost();

        QMessageBox::information(this, "ok", "文件接收完毕");
        ui->textEdit->append("文件接收完毕");
    }
}

//连接按钮
void ClientWidget::on_buttonConnect_clicked()
{
    ui->progressBar->setValue(0);

    QString ip = ui->lineEditIP->text();
    qint16 port = ui->lineEditPort->text().toInt();
    if(ip.isEmpty() == true || port == 0)
    {
        QMessageBox::warning(this, "警告", "ip或端口不能为空");
        return;
    }

    tcpSocket->abort(); //取消已有的连接

    //连接服务器
    tcpSocket->connectToHost(QHostAddress(ip), port);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

server.h

#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H

#include <QWidget>
#include <QtNetwork> //网络相关头文件
#include <QFile>
#include <QTimer>

namespace Ui {
class ServerWidget;
}

class ServerWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ServerWidget(QWidget *parent = 0);
    ~ServerWidget();

    /**
     * @brief sendFileData  发送文件数据函数
     */
    void sendFileData();

private slots:
    /**
     * @brief slot_Timerstart   定时器启动*毫秒调用此槽函数
     */
    void slot_TimerStart();

    /**
     * @brief slot_NewConnection    当有新数据到来时调用此槽函数
     */
    void slot_NewConnection();

private slots:
    /**
     * @brief on_buttonChoose_clicked   选择文件按钮
     */
    void on_buttonChoose_clicked();

    /**
     * @brief on_buttonSend_clicked     发送文件按钮
     */
    void on_buttonSend_clicked();



private:
    Ui::ServerWidget *ui;

    QTcpServer *tcpServer;  //监听套接字
    QTcpSocket *tcpSocket;  //通信套接字
    QFile file;             //文件对象
    QString fileName;       //文件名字
    qint64 fileSize;        //文件大小
    qint64 sendSize;        //已发送文件的大小

    QTimer timer;           //定时器
};

#endif // SERVERWIDGET_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

server.cpp

#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QTimer>
#include <QThread>

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

    //创建套接字
    tcpServer = new QTcpServer(this);

    //监听,端口:8888
    bool isOk = tcpServer->listen(QHostAddress::Any, 8888);
    if(false == isOk)//监听失败
    {
        QMessageBox::warning(this, "监听", "监听失败");
        return;
    }

    //设置标题
    setWindowTitle("服务器:8888");

    //设置按钮(变灰)
    ui->buttonChoose->setEnabled(false);
    ui->buttonSend->setEnabled(false);

    //当有客户端链接时,触发信号:newConnection
    connect(tcpServer,&QTcpServer::newConnection,this,&ServerWidget::slot_NewConnection);
    //定时器处理函数
    connect(&timer,&QTimer::timeout,this,&ServerWidget::slot_TimerStart);
}

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

//选择文件按钮
void ServerWidget::on_buttonChoose_clicked()
{
    QString path = QFileDialog::getOpenFileName(this, "请选择所要发送的文件", "../");
    if(path.isEmpty() == false)//路径有效
    {
        //file为成员变量
        file.setFileName(path); //设置文件路径
        //只读方式打开文件
        bool isOk = file.open(QIODevice::ReadOnly);
        if(false == isOk)//打开文件失败
        {
            QMessageBox::warning(this, "警告", "打开文件失败");
            return;
        }
        else
        {
            ui->textEdit->append("发送的文件:");
            ui->textEdit->append(path);

            //初始化数据
            fileName = "";
            fileSize = 0;
            sendSize = 0;

            //获取发送文件的信息
            QFileInfo info(path);
            fileName = info.fileName();  //文件名
            fileSize = info.size();      //文件大小

            ui->buttonSend->setEnabled(true); //恢复发送文件按钮
            ui->buttonChoose->setEnabled(false); //选择文件按钮变灰
        }
    }
}

//发送文件按钮
void ServerWidget::on_buttonSend_clicked()
{
    // 发送文件按钮变灰
    ui->buttonSend->setEnabled(false);

    //先发送文件头,自定义的数据,不是文件数据
    //先发送头,自定义组包, 文件名#文件大小
    QString buf = QString("head#%1#%2").arg(fileName).arg(fileSize);

    //先发头
    qint64 len = tcpSocket->write( buf.toUtf8().data());
    tcpSocket->waitForBytesWritten(); //等待数据发送完毕

    ui->textEdit->append("已经在发送文件!!!");

    if(len > 0) //如果头部信息发送成功,开始发送文件数据
    {
        //10毫秒后再发送文件数据
        //启动定时器,定时器内容发送文件数据
        //防止TCP黏包问题
        this->timer.start(1);
    }
    else
    {
        file.close(); //关闭文件
    }
}

void ServerWidget::sendFileData()
{
    //循环读取数据发送
    qint64 len = 0;
    do{
        // 每次发送 2kb 大小的数据,如果剩余的数据不足 2kb,就发送剩余数据的大小
        char buf[2*1024] = {0};

        len = 0;
        len = file.read( buf, sizeof(buf) ); //读数据
        len = tcpSocket->write(buf, len);    //发数据

        sendSize +=len; //已发送的文件数据大小
    }while(len > 0);

    //文件数据发送完毕
    if(sendSize == fileSize)
    {
        //QMessageBox::information(this, "ok", "文件发送完毕");
        ui->textEdit->append("文件发送完毕");

        //关闭文件
        file.close();
        //关闭客户端
        tcpSocket->disconnectFromHost();
        tcpSocket->close();
    }
}

void ServerWidget::slot_TimerStart()
{
    this->timer.stop(); //关闭定时器
    sendFileData();     //发送文件数据
}

void ServerWidget::slot_NewConnection()
{
    //取出链接套接字
    tcpSocket = tcpServer->nextPendingConnection();

    //客户端IP和端口
    QString ip = tcpSocket->peerAddress().toString();
    qint16 port = tcpSocket->peerPort();
    QString str = QString("[%1:%2]和服务器连接成功").arg(ip).arg(port);
    ui->textEdit->setText(str); //设置内容

    //恢复选择按钮状态
    ui->buttonChoose->setEnabled(true);

    QMessageBox::information(this, "允许", "连接成功,可以选择文件发送");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159

测试结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

over:

欢迎大家关注作者在文末评论、点赞、转发以及批评指正!
如果大家有更好的方法或有问题可以在文末评论一起讨论!
共同学习!
共同进步!

文末一句话:

如今最好,别说来日方长,时光难留,只有一去不返。。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/305160?site
推荐阅读
相关标签
  

闽ICP备14008679号