49黑马QT笔记之利用TCP传输文件

2023-10-28

49黑马QT笔记之利用TCP传输文件

前提:
黑马视频的代码第二次不能传输的原因是因为:客户端在第一次传输完成后,isStart=false。即第二次无法接收文件信息并打开文件,所以他会出现"write:device not open"。我们在文件接收完毕后,即文件相等那里重置isStart=true即可。

1 服务端流程:
1) 正常连接通信。
2) 选择文件按钮,弹出对话框选择文件,在该槽函数初始化文件信息info。包括文件名,大小,文件对象,已发送数据大小等等。
3) 发送文件按钮,先组包发送一次文件信息。(发送成功就利用定时器延时一定秒数,再发送完整的文件数据。实现延时的操作就是把原本要写的内容写在timeout信号的槽函数中。)
4) 发送完整的文件数据—发送数据先file.read读出来,再write发送过去,循环读和发送。最后再判断发送的数据和文件大小是否相同即可。

注:
1)服务端是发送了两次文件信息,第一次只是文件信息;第二次包括文件信息和数据。延时是为了防止黏包。
2)文件头与文件信息不对等,文件头是文件前54字节,包括文件各种信息。这里的例子文件信息只是自定义指文件名和文件大小。

2 客户端流程:
1 网络连接(tcpsocket,connect按钮)。
2 读文件信息和整个文件:
– 1)若为文件信息 —初始化接收文件的信息并打开接收文件。
– 2)若为整个文件 —写进文件对象file(不用while,因为有readyread),当recvSize==fileSize时,用信息框提示文件接收完成并重置isStart,为下一次连接传输作准备。

注:接收文件信息和整个文件用isStart标志位区分。默认接收文件路径在当前文件夹,不同环境可能不同。

3 代码:
1)服务端头文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QTcpServer>
#include<QTcpSocket>
#include<QFile>
#include<QTimer>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
    void sendData();   //自己封装一个发送文件数据的函数

private slots:
    void on_buttonFile_clicked();

    void on_buttonsend_clicked();

private:
    Ui::Widget *ui;
    QTcpServer *tcpServer;
    QTcpSocket *tcpSocket;

    QFile file;         //文件对象
    QString fileName;   //文件名字
    qint64 fileSize;    //文件大小
    qint64 sendSize;   //已经发生文件的大小

    QTimer timer;     //定时器
};

#endif // WIDGET_H

2)服务端实现文件:

#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
#include<QIODevice>
#include<qDebug>


//1 TCP连接
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //1监听套接字
    tcpServer=new QTcpServer(this);

    //2监听
    tcpServer->listen(QHostAddress::Any,8888);
    setWindowTitle("服务器端口为:8888");

    //没连接之前两个按钮都不能按
    ui->buttonFile->setEnabled(false);
    ui->buttonsend->setEnabled(false);

    //3如果客户端成功和服务器连接
    //tcpServer会自动触发newConnection()

    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);                       //1 以上三步为了在文本编辑区显示客户端  ip和端口

        //成功连接后,才能选择文件
        ui->buttonFile->setEnabled(true);
    }

            );
    connect(&timer,&QTimer::timeout,
            [=]()
    {
            //关闭定时器
            timer.stop();

            //发送文件
            sendData();
    }



            );
}

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


//2 选择文件按钮---初始化
void Widget::on_buttonFile_clicked()
{
    QString filePath=QFileDialog::getOpenFileName(this,"open","../");
    if(false==filePath.isEmpty())   //如果选择文件路径有效
    {
        fileName.clear();
        fileSize=0;

        //获取文件信息
        QFileInfo info(filePath);
        fileName=info.fileName();
        fileSize=info.size();

        sendSize=0;

        //只读方式打开
        //指定文件的名字
        file.setFileName(filePath);

        //打开文件
        bool isOk=file.open(QIODevice::ReadOnly);
        if(false==isOk)
        {
            qDebug() <<"只读方式打开文件失败 75";
            return;
        }

        //提示打开文件的路径
        ui->textEdit->append(filePath);

        ui->buttonFile->setEnabled(false);
        ui->buttonsend->setEnabled(true);

    }
    else
    {
        qDebug()<<"选择文件路径出错 82";
        return;
    }
}


//3 发送文件按钮--文件头
void Widget::on_buttonsend_clicked()
{

    //按下发送给它不能再按 因为发送一次就断开一次连接
    ui->buttonsend->setDisabled(true);

    //先发送文件头信息 文件名##文件大小
    QString head=QString("%1##%2").arg(fileName).arg(fileSize);

    //发送头部信息
    qint64 len=tcpSocket->write(head.toUtf8().data());

    if(len>0)  //头部信息成功发生
    {
        //发生真正的文件信息
        //防止TCP粘包文件
        //需要通过定时器延时20ms

        //实现延时的操作就是把原本要写的内容写在timeout信号的槽函数中。
        timer.start(20);

    }
    else
    {
        qDebug()<<"头部信息大小:"<<len;
        qDebug()<<"头部信息发生失败 137";
        file.close();
//      tcpSocket->disconnectFromHost();
//      tcpSocket->close();                          //2 不用关连接,只需要关闭文件让他在选择文件按钮 发送文件(会跳回send按钮)就好
        ui->buttonFile->setEnabled(true);
        ui->buttonsend->setEnabled(false);
    }


}

//4 开始发送文件--也包括文件头54个字节和真正数据
//  前面发送文件信息是为了给客户端要接收多大的文件
void Widget::sendData()
{
    qint64 len=0;
    do
    {
        //每次发送数据的大小
        char buf[4096]={0};
        len=0;

        //往文件中读数据,返回实际读到的字节数
        len=file.read(buf,sizeof(buf));              //3 读只有这一种,写有单参数和双参数两种
        //发送数据,读多少,发多少
        len=tcpSocket->write(buf,len);               //4 与发送头部信息的单参数有区别,其实上面也可用两个参数的

        //发送的数据需要累积
        sendSize+=len;


    }while(len>0);                                   //5 如果写过去的数据为0,则表示文件写完,do while()结束


    //是否发送文件完毕
    if(sendSize==fileSize)              //6 其实这个判断我感觉多余了,因为已经结束了,提示发送完毕然后直接关闭文件和连接就好了,保险起见吧
    {
        ui->textEdit->append("!!!文件发送完毕!!!");
        file.close();

        //把客户端断开
        tcpSocket->disconnectFromHost();             //7 这次要全部断开了
        tcpSocket->close();
    }




}

3)客户端头文件:

#ifndef CILENT_H
#define CILENT_H

#include <QWidget>
#include<QTcpSocket>
#include<QFile>

namespace Ui {
class cilent;
}

class cilent : public QWidget
{
    Q_OBJECT

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

private slots:
    void on_pushButton_clicked();    //connect按钮,忘记改名了

private:
    Ui::cilent *ui;

    QTcpSocket *tcpSocket;

    QFile file;         //文件对象
    QString fileName;   //文件名字
    qint64 fileSize;    //文件大小
    qint64 recvSize;   //已经接收文件的大小

    bool isStart;     //用于区分接收文件头和文件数据
};

#endif // CILENT_H

4)客户端实现文件:

#include "cilent.h"
#include "ui_cilent.h"
#include<QIODevice>
#include<QMessageBox>
#include<QHostAddress>

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

    tcpSocket=new QTcpSocket(this);

    isStart=true;

    //等待对方发送数据---构造时不会执行 只有readyRead触发才会执行--所以connect你可以认为不是顺序执行
    connect(tcpSocket,&QTcpSocket::readyRead,
            [=]()
    {
        //1)取出接收的内容
        QByteArray buf=tcpSocket->readAll();

        //接收文件头
        if(true==isStart)
        {
            //下一次接收整个文件
            isStart=false;


            //初始化
            fileName=QString(buf).section("##",0,0);
            file.setFileName(fileName);
            fileSize=QString(buf).section("##",1,1).toInt();
            recvSize=0;


            //打开文件
            bool isOk=file.open(QIODevice::WriteOnly);
            if(false==isOk)
            {
                qDebug()<<"WriteOnly error 37";
            }
        }
        //2)接收文件信息
        else
        {

               //file.read(buf);                                        //写进file应用write 读进buf应用read
               qint64 len=file.write(buf);                              //写buf里的数据到设备file 
               recvSize+=len;

               if(recvSize==fileSize)                                  //相等则用信息框提示接收完成 然后关闭文件和连接
               {
                   file.close();
                   QMessageBox::information(this,"完成","文件接收完成");

                   tcpSocket->disconnectFromHost();
                   tcpSocket->close();

                   //使第二次仍能打开 解决"write:device not open"
                   isStart=true;
               }
        }
    });


}

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


//connect按钮只用于连接
void cilent::on_pushButton_clicked()
{
    //获取服务器ip和端口号
    QString ip=ui->lineEdit->text();
    quint16 port=ui->lineEdit_2->text().toInt();


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

4 服务端与客户端的ui界面:
在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

49黑马QT笔记之利用TCP传输文件 的相关文章

  • 57黑马QT笔记之数据库的语句执行--插入(包括SQL语句的增删改查和复制表结构)

    57黑马QT笔记之数据库的语句执行 插入 概念 1 回滚 也就是我们常见的事务回滚 例如当我要删除某个数据库时 我开启了一个事务 然后再弹出一个对话框给你 是否提交事务 或者作出事务的回滚 也就是说取消了这个删除的操作 并不会执行到删除数据
  • 27黑马QT笔记之QPixmap和QBitmap的区别

    27黑马QT笔记之QPixmap和QBitmap的区别 前提 首先我们先要了解什么是透明和不透明的图片 1 透明是指显示后与背景颜色一样的 2 不透明就是有自己的颜色 与背景颜色不一样 看下图 前者就是透明的 后者是背景是白色的 是不透明的
  • 45黑马QT笔记之Qt下的Udp的广播和组播

    45黑马QT笔记之Qt下的Udp的广播和组播 1 广播 在使用QUdpSocket类的writeDatagram 函数发送数据的时候 其中第二个参数host应该指定为广播地址 QHostAddress Broadcast此设置相当于QHos
  • 24黑马QT笔记之画背景图

    24黑马QT笔记之画背景图 1 区别不是窗口的显示图片 在窗口中绘图必须重写绘图事件 在绘图事件中绘图 即用到QPainter画家和xxx begin this 函数指定绘图设备 与之前学的区别 下面的代码是在标签中显示图片 只是简单的显示
  • 49黑马QT笔记之利用TCP传输文件

    49黑马QT笔记之利用TCP传输文件 前提 黑马视频的代码第二次不能传输的原因是因为 客户端在第一次传输完成后 isStart false 即第二次无法接收文件信息并打开文件 所以他会出现 write device not open 我们在
  • 60黑马QT笔记之SQLite

    60黑马QT笔记之SQLite 1 与MYSQL的区别 1 SQLite是本地数据库 不需要和MYSQL一样需要连接 2 插入时不支持自动增长 所以主键例如id 在输入时需要自己去控制 3 使用时需要先提前建好后缀为xxx db的文本文件
  • 44黑马QT笔记之IP地址的划分与是否在同一网段

    44黑马QT笔记之IP地址的划分与是否在同一网段 前提 1 网络ID ip地址的第一个字节 2 网络地址 在这里你可以认为它就是网络ID 3 网段 用来区分网络上的主机是否在同一区段内 只要知道ip地址和子网掩码就知道该网段 在局域网中只有
  • 31黑马QT笔记之QPixmap、QImage、QPicture功能大总结

    31黑马QT笔记之QPixmap QImage QPicture功能大总结 QPixmap QImage既可以用于绘图 又可以作绘图设备时保存图片 而QPicture只能用于作绘图设备时保存图片状态 保存前需要用到前两种方法绘图 要显示必须
  • 28黑马QT笔记之QPixmap保存图片

    28黑马QT笔记之QPixmap保存图片 1 QPixmap QImage QPictrue三种绘图工具之间的区别 上一篇我们讲到QPixmap如何在窗口进行绘图 接下来本篇要讲的是QPixmap如何保存一张图片 即将已有图片再按自己方式保
  • 35黑马QT笔记之QFile写文件

    35黑马QT笔记之QFile写文件 1 如何在文本编辑区写内容保存到一个本地文件呢 1 利用文件对话框函数getSaveFileName获取要创建的文件路径 实际上还没真正在电脑创建 只是意味着你要创建的路径 2 将要创建的文件路径与QFi
  • 42黑马QT笔记之Linux下Tcp/Udp通信过程

    42黑马QT笔记之Linux下Tcp Udp通信过程 1 Linux下Tcp通信过程 1 第一次握手 执行connect 2 第二次握手 accept 返回 3 第三次握手 connect 返回 4 共有三个套接字 客户端1个fd 服务端一
  • 23黑马QT笔记之猜数字游戏答案

    23黑马QT笔记之猜数字游戏答案 代码在自己写的day04的第一个项目 想要代码的直接评论 写上自己的邮箱 不要像以前发私信了 因为CSDN有时消息不同步 或者看了之后忘了
  • 47黑马QT笔记之Qt下Udp通信过程例子及实现广播与组播

    47黑马QT笔记之Qt下Udp通信过程例子及实现广播与组播 1 客户端 客户端需要注意一点 客户端没有绑定端口的话 服务端无法发送信息给客户端 Linux下可以是因为他们在同一端口 所以Qt下你也在同一端口内通信 即窗口内自己连自己的Ip和
  • 36黑马QT笔记之QString、QByteArray、char*的互相转换

    36黑马QT笔记之QString QByteArray char 的互相转换 1 直接看代码 if 0 QString gt QByteArray QString str 123 QByteArray array str toUtf8 中文
  • 37黑马QT笔记之QFileInfo提供文件相关信息

    37黑马QT笔记之QFileInfo提供文件相关信息 1 QFileInfo 这个类提供了许多函数给我们查找文件的信息 例如文件名 文件大小等等 对我们进行某些处理相当有用 例如你要对某个目录操作 需要判断它是否为目录 当你需要传输文件内容
  • 07黑马QT笔记之信号重载时connect的写法(带参数的信号)

    07黑马QT笔记之信号重载时connect的写法 带参数的信号 1 首先说这个例子要做的事情 一个窗口有两个按钮 分别为按钮1 按钮2 当我按下按钮2时 他会发射两个信号 这两个信号重载 名字一样 所以当我接收这两个信号并处理时 conne
  • 43黑马QT笔记之Qt下Tcp/Udp通信过程

    43黑马QT笔记之Qt下Tcp Udp通信过程 前提 Qt下的网络通信需要加上 QT newwork 模块 1 Qt下Tcp的通信过程 1 共有三个套接字 客户端有一个QTcpServer监听套接字 服务端有两个 分别是QTcpServer
  • 32黑马QT笔记之QPixmap和QImage的相互转换

    32黑马QT笔记之QPixmap和QImage的相互转换 1 QPixmap与QImage的互相转换 1 头文件 void paintEvent QPaintEvent 2 实现 cpp文件 void Widget paintEvent Q
  • 22黑马QT笔记之事件全总结

    22黑马QT笔记之事件全总结 1 每个控件重写过滤器 event函数 各个事件处理函数都一样 都是先类中声明 类外定义 2 每个控件都可以重写事件过滤器 但是他一般写在窗口 安装时参数要求继承QObject嘛 event函数和各个事件处理函
  • 55黑马QT笔记之关闭子线程

    55黑马QT笔记之关闭子线程 1 这里为什么要单独写多一篇文章来说线程的关闭呢 主要是想让大家提升印象 养成资源回收的好习惯 任何时候都要想起开辟过的内存回收 这里的关闭子线程上一篇也写到了 就是利用关闭窗口时调用槽函数回收掉 2 具体步骤

随机推荐

  • 关于Android的自动化测试,你需要了解的5个测试框架

    Appium Appium是一个开源的移动测试工具 支持iOS和Android 它可以用来测试任何类型的移动应用 原生 网络和混合 作为一个跨平台的工具 你可以在不同的平台上运行相同的测试 为了实现跨平台的功能 Appium使用了供应商提供
  • 无法启动 nexus 服务,错误1067:进程意外终止。java环境变量设置技巧。

    Nexus启动失败 wrapper log记载 无支持版本 51 0 版本51 0指的是Java1 7 分析 nexus版本为2 14 8 适用JRE版本为1 7 已配置JAVA HOME为1 7版 cmd中 java version 显示
  • 【ASE学习笔记】_02_溶解

    Shader类型 实现效果 最终结点 思路 通过控制图片的不透明度来实现图片的展示或消失 再通过去控制不透明度的形状让其无规则的呈现 这里利用噪声图 1 设置一个DIss系数用于控制噪声图的显示 通过Step函数 B gt A的部分会被显示
  • 详解nuitka打包

    安装和说明 说明 为什么学习nuitka 一个字 小 使用nuitka打包的exe体积明显要比pyinstaller小很多 且启动速度要快个30 左右 本着速度即是正义 因此没有特殊需求过短抛弃pyinstaller拥抱nuitka 安装和
  • Qt为工具栏按钮QToolButton添加下拉菜单

    效果如下 主要代码如下 MainWindow MainWindow QWidget parent QMainWindow parent ui new Ui MainWindow ui gt setupUi this createToolBu
  • Maven云笔记之登录注册

    Maven云笔记注册登录篇 使用MVC框架开始编写一个笔记本应用 目录结构 登录界面
  • 关于智能车独轮组编码器的思考

    独轮车模到货了 计划使用更优雅更便宜的霍尔编码器而不用常规的机械编码器 所以立刻对霍尔编码器可行性进行了评估 O车模减速电机出轴特写 减速电机大概就是加了减速齿轮组的RS380 是标准尺寸电机 所以配套的编码器市面上肯定有卖 淘宝稍稍一翻便
  • 前后端分离项目简单实现增删改查

    大家好 我是梦想皮皮 今天带来一篇关于springboot vue MySQL项目的粗浅的介绍 同时也用到了vue element也集成了mybatis希望能帮助到大家 该项目使用到springboot vue MySQL mybatis同
  • 如何读懂期权希腊字母、隐含波动率、理论价格等?

    如何读懂期权希腊字母 隐含波动率 理论价格等 每一个希腊值都是用来度量期权头寸的某种特定风险 投资者可以通过管理这些希腊值 以 便将风险保持在一个可以接受的范围之内 Delta 期权价格变化同标的价格变化之间的比率 看涨期权的 Delta
  • r连接db2 no license is present_R语言基础 期中考试

    一 单选题 题数 20 共 40 0 分 1下列用来转换数据框的函数是 2 0分 2 0 分A as listB as matrixC as data frameD as vector我的答案 C2以下函数不能直接查看plot函数的帮助文档
  • 检测CPU是否具备VT的功能,并开启

    1 cpuid 指令 EFLAGS寄存器中的ID标志 位21 表示对CPUID指令的支持 如果一个软件程序可以设置和清除这个标志 那么执行该程序的处理器支持CPUID指令 这条指令在非64位模式和64位模式下操作相同 CPUID在EAX E
  • 华为手机计算机代码大全,华为手机指令代码大全,你想要了解的功能都在这里!...

    原标题 华为手机指令代码大全 你想要了解的功能都在这里 昨天写了vivo手机的指令代码 有部分朋友说不够详细 以及不懂这些是干嘛的 其实 你按照文中去操作一遍就能知道这些指令的功能了啊 今天来说说华为 华为手机大部分手机都采用的是安卓系统
  • Vue.js 学习笔记十六:Axios 之 Axios 封装

    目录 Axios 封装 Axios 封装 src plugins http js import axios from axios import router from router import store from store impor
  • Unity在运行时使用FBX SDK的API

    1 写在前面 本文主要一块学习关于Unity官方封装的AutoDesk的FBX SDK的API的C 版本包 最初该包的本意是想让开发者在编辑器模式下使用 但是官方也提供了在运行时对于该包的支持 2 获取Autodesk FBX SDK fo
  • 竞赛 基于机器视觉的手势检测和识别算法

    0 前言 优质竞赛项目系列 今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖 适合作为竞赛课题方向 学长非常推荐 更多资料 项目分享 https gitee com dancheng senior postgraduate
  • LeetCode:228(Python)—— 汇总区间(简单)

    汇总区间 概述 给定一个无重复元素的有序整数数组 nums 返回恰好覆盖数组中所有数字的最小有序区间范围列表 也就是说 nums 的每个元素都恰好被某个区间范围所覆盖 并且不存在属于某个范围但不属于 nums 的数字 x 输入 nums 0
  • js逆向补环境示例代码1

    在官方文档 内置对象中的object下很多属性都可以用来检测环境 Object JavaScript MDN 拿Object getOwnPropertyDescriptor 来做示例笔记 在文档中它的说明 Object getOwnPro
  • matlab erf erfi,误差函数

    误差函数 在数学中 误差函数 也称之为高斯误差函数 是一个特殊函数 即不是初等函数 其在概率论 统计学以及偏微分方程中都有广泛的应用 它的定义如下 erf x 1 x x e t 2 d t 2 0 x e t 2 d t displays
  • 关于pthread_rwlock_t读写锁产生死锁的情况

    对于pthread rwlock t读写锁 一个线程持有着写锁 又去加 该锁的 读锁 可能会产生死锁 一个线程持有着 读或者写 锁 又去加 该锁的 写锁 可能会产生死锁 此时pthread rwlock rdlock和pthread rwl
  • 49黑马QT笔记之利用TCP传输文件

    49黑马QT笔记之利用TCP传输文件 前提 黑马视频的代码第二次不能传输的原因是因为 客户端在第一次传输完成后 isStart false 即第二次无法接收文件信息并打开文件 所以他会出现 write device not open 我们在