| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> QT tcp服务器及客户端 传输图片及显示 -> 正文阅读 |
|
[网络协议]QT tcp服务器及客户端 传输图片及显示 |
上一节里我们使用TCP服务器和客户端相互发送字符串信息,本节间学习图片的传输及显示。 一、服务器端 程序开始进行监听,一旦发现有连接请求就发出newConnection()信号,然后我们便接受连接,可以接收数据。 步骤1.新建QtGui应用 名称为tcpServer,基类选择QWidget,类名为Widget,完成后打开tcpReceiver.pro添加一行代码:QT += network 。 步骤2.我们更改widget.ui文件,设计界面如下。 其中“服务器端”Label的objectName为statuslab;进度条ProgressBar的objectName为progressBar,设置其value属性为0;发送按钮的objectName为sendButton;图片显示textlabel的objectName为recLab。 效果如下。 步骤3.更改widget.h文件的内容。
(2)添加私有变量:
??? QTcpSocket *currentClient; ??? qint64 totalBytes;? //存放总大小信息 ??? qint64 bytesReceived;? //已收到数据的大小 ??? qint64 fileNameSize;? //文件名的大小信息 ??? QString fileName;?? //存放文件名 ??? QFile *localFile;?? //本地文件 ??? QByteArray inBlock;?? //接收数据缓冲区 ??? qint64 bytesWritten;? //已经发送数据大小 ??? qint64 bytesToWrite;?? //剩余数据大小 ??? qint64 loadSize;?? //每次发送数据的大小 ??? QByteArray outBlock;? //数据缓冲区 (3)添加私有槽函数: private?slots:
??? void recMessage(); ??? void sendMessage(); ??? void disconnect(); ??? void continueSend(qint64); ??? void on_sendButton_clicked(); 步骤4.更改widget.cpp文件。 (1)在构造函数中添加代码: totalBytes = 0; ? ??bytesReceived = 0; fileNameSize = 0; totalBytes = 0; ??? bytesReceived = 0; ??? fileNameSize = 0; ??? tcpServer = new QTcpServer(this); ?? ?if(!tcpServer->listen(QHostAddress::Any,6666)) ??? {? //**本地主机的6666端口,如果出错就输出错误信息,并关闭 ??????? qDebug() << tcpServer->errorString(); ??????? close(); ??? } ??? //连接信号和相应槽函数,有新的连接进入是需处理 ??? connect(tcpServer,SIGNAL(newConnection()),this,SLOT(NewConnection())); ??? ui->sendButton->setEnabled(false); (2)实现接受连接函数。 void Widget::NewConnection() { ??? //初始化为0; ??? //blockSize=0; ?? // inBlock.resize(0); ??? //新连接进入的显示处理 ??? currentClient = tcpServer->nextPendingConnection(); ??? ui->statuslab->setText(tr("%1:%2").arg(currentClient->peerAddress().toString().split("::ffff:")[1])\ ????????????????????????????????????????????? .arg(currentClient->peerPort())); ??? connect(currentClient, SIGNAL(readyRead()), this, SLOT(recMessage())); ??? connect(currentClient, SIGNAL(disconnected()), this, SLOT(disconnect())); ??? //当有数据发送成功时,继续发送 ??? connect(currentClient,SIGNAL(bytesWritten(qint64)),this, SLOT(continueSend(qint64))); ??? ui->sendButton->setEnabled(true); } 我们要先发送文件的总大小,然后文件名长度,然后是文件名,这三部分合称为文件头结构,最后再发送文件数据。所以在发送函数里就要进行相应的处理,当然,在服务器的接收函数里也要进行相应的处理。对于文件大小,这次使用了qint64,它是64位的,可以表示一个很大的文件。 (3)开始发送数据(文件名,文件大小等信息) void Widget::sendMessage() { ??? bytesWritten = 0; ??? fileName = QFileDialog::getOpenFileName(this); ??? if(!fileName.isEmpty()) ????? { ??????? localFile = new QFile(fileName); ??????????? if(!localFile->open(QFile::ReadOnly)) ??????????? { ?????????????? qDebug() << "open file error!"; ?????????????? return; ??????????? } ??????????? //文件总大小 ??????????? totalBytes = localFile->size(); ??????????? QDataStream sendOut(&outBlock,QIODevice::WriteOnly); ??????????? sendOut.setVersion(QDataStream::Qt_5_8); ??????????? QString currentFileName = fileName.right(fileName.size() ??????? - fileName.lastIndexOf('/')-1); ??????????? //依次写入总大小信息空间,文件名大小信息空间,文件名 ??????????? sendOut << qint64(0) << qint64(0) << currentFileName; ??????????? //这里的总大小是文件名大小等信息和实际文件大小的总和 ??????????? totalBytes += outBlock.size(); ??????????? sendOut.device()->seek(0); ??????????? //返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间 ??????????? sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2)); ??????????? //发送完头数据后剩余数据的大小 ??????????? bytesToWrite = totalBytes - currentClient->write(outBlock); ??????????? ui->statuslab->setText(tr("开始发送")); ??????????? outBlock.resize(0); ????? } ?} (4)分块发送,更新进度条 void Widget::continueSend(qint64 numBytes) ?{ ???? //已经发送数据的大小 ???????? bytesWritten += (int)numBytes; ???????? if(bytesToWrite > 0) //如果已经发送了数据 ???????? { ??????? //每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB, ??????? //就发送剩余数据的大小 ??????????? outBlock = localFile->read(qMin(bytesToWrite,loadSize)); ??????????? //发送完一次数据后还剩余数据的大小 ??????????? bytesToWrite -= (int)currentClient->write(outBlock); ??????????? //清空发送缓冲区 ??????????? outBlock.resize(0); ???????? } else { ??????????? localFile->close(); //如果没有发送任何数据,则关闭文件 ???????? } ???????? //更新进度条 ???????? ui->progressBar->setMaximum(totalBytes); ???????? ui->progressBar->setValue(bytesWritten); ???????? if(bytesWritten == totalBytes) //发送完毕 ???????? { ????????? ui->statuslab->setText(tr("传送文件 %1 成功").arg(fileName)); ??????????? localFile->close(); ???????? } } (5)发送按钮 void Widget::on_sendButton_clicked() { ??? //发送数据 ??? sendMessage(); } (6)接收及显示 void Widget::recMessage() { ??? QDataStream in(currentClient); ??? in.setVersion(QDataStream::Qt_5_8); ??? if(bytesReceived <= sizeof(qint64)*2) ????? { //如果接收到的数据小于16个字节,那么是刚开始接收数据,我们保存到//来的头文件信息 ???????? if((currentClient->bytesAvailable() >= sizeof(qint64)*2) ?????????????? && (fileNameSize == 0)) ?????????? { //接收数据总大小信息和文件名大小信息 ?????????????? in >> totalBytes >> fileNameSize; ?????????????? bytesReceived += sizeof(qint64) * 2; ?????????? } ?????????? if((currentClient->bytesAvailable() >= fileNameSize) ?????????????? && (fileNameSize != 0)) ?????????? {? //接收文件名,并建立文件 ?????????????? in >> fileName; ?????????????? ui->statuslab->setText(tr("接收文件 %1 ...").arg(fileName)); ?????????????? bytesReceived += fileNameSize; ?????????????? ui->statuslab->setText(fileName); ?????????????? localFile= new QFile(fileName); ?????????????? if(!localFile->open(QFile::WriteOnly)) ?????????????? { ??????????????????? qDebug() << "open file error!"; ????? ??????????????return; ?????????????? } ?????????? } ?????????? else return; ?????? } ?????? if(bytesReceived < totalBytes) ?????? {? //如果接收的数据小于总数据,那么写入文件 ????????? bytesReceived += currentClient->bytesAvailable(); ????????? inBlock+= currentClient->readAll(); ?????? } ??? //更新进度条 ?????? ui->progressBar->setMaximum(totalBytes); ?????? ui->progressBar->setValue(bytesReceived); ?????? if(bytesReceived == totalBytes) ?????? { //接收数据完成时 ?????????? //接收显示 ?????????? QBuffer buffer(&inBlock); ?????????? buffer.open(QIODevice::ReadOnly); ?????????? QImageReader reader(&buffer,"jpg"); ?????????? QImage image = reader.read(); ?????????? if(!image.isNull()) ?????????? { ?????????????? image=image.scaled(ui->recLab->size()); ?????????????? ui->recLab->setPixmap(QPixmap::fromImage(image)); ?????????? } ??????? localFile->write(inBlock); ??????? localFile->close(); ??????? inBlock.resize(0); ??????? //重新置0 准备下次接收 ??????? totalBytes = 0; ??????? bytesReceived = 0; ??????? fileNameSize = 0; ?????? ??ui->statuslab->setText(tr("接收文件 %1 成功!").arg(fileName)); ?????? } } 二、客户端 客户端里需要与服务器进行连接,一旦连接成功,才可以进行文件传输。 步骤1.新建QtGui项目 名称为tcpSender,基类选择QWidget,类名为Widget,完成后打开tcpSender.pro添加一行代码:QT += network 。 步骤2.我们在widget.ui文件中将界面设计如下。 这里“主机”后的Line Edit的objectName为ipEdit;“端口”后的Line Edit的objectName为portEdit;下面的Progress Bar的objectName为progressBar,其value属性设为24;“状态”Label的objetName为statusLab;“打开”按钮的objectName为conBun;“发送”按钮的objectName为sendBun。 步骤3.在widget.h文件中进行更改。 (1)添加头文件包含#include?<QtNetwork> (2)添加private变量: QTcpSocket *tcpSocket; ??? QFile *localFile;? //要发送的文件 ??? qint64 totalBytes;? //数据总大小 ??? qint64 bytesWritten;? //已经发送数据大小 ??? qint64 bytesToWrite;?? //剩余数据大小 ??? qint64 loadSize;?? //每次发送数据的大小 ??? QString fileName;? //保存文件路径 ??? QByteArray outBlock;? //数据缓冲区,即存放每次要发送的数据 ??? QByteArray inBlock;?? //数据缓冲区,接收 ??? qint64 bytesReceived;? //已收到数据的大小 ??? qint64 fileNameSize;? //文件名的大小信息 (3)添加私有槽函数:
??? void readData();? //接收数据 ??? void sendData();//发送数据 ??? void continueSend(qint64 numBytes); //继续并更新发送进度条 ??? void on_conBtn_clicked(); ??? void on_sendbtn_clicked(); 步骤4.在widget.cpp文件中进行更改 添加头文件:#include?<QFileDialog> (1)在构造函数中添加代码: tcpSocket = new QTcpSocket(this); ??? loadSize = 4*1024; ??? totalBytes = 0; ??? bytesWritten = 0; ??? bytesToWrite = 0; ??? //接收 ??? bytesReceived = 0; ??? fileNameSize = 0; ??? //当有数据发送成功时,继续发送 ??? connect(tcpSocket,SIGNAL(bytesWritten(qint64)),this, ?????????? SLOT(continueSend(qint64))); ??? //接收数据 ??? connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readData())); ??? ui->sendbtn->setEnabled(false); 该函数将在下面的“打开”按钮单击事件槽函数中调用。 (2)实现连接主机函数。 void Widget::newConnect() { ? //? blockSize = 0; //初始化其为0 ??? tcpSocket->abort(); //取消已有的连接 ??? //连接到主机,这里从界面获取主机地址和端口号 ??? tcpSocket->connectToHost(ui->hostEdit->text(), ui->portEdit->text().toInt()); ??? ui->sendbtn->setEnabled(true); }该函数将在“发送”按钮的单击事件槽函数中调用。 void Widget::on_conBtn_clicked() { ??? newConnect(); } (4)实现文件头结构的发送。 void Widget::sendData() { ?? bytesWritten = 0; ?? fileName = QFileDialog::getOpenFileName(this); ?? if(!fileName.isEmpty()) ???? { ?? ????localFile = new QFile(fileName); ?????????? if(!localFile->open(QFile::ReadOnly)) ?????????? { ????????????? qDebug() << "open file error!"; ????????????? return; ?????????? } ?????????? //文件总大小 ?????????? totalBytes = localFile->size(); ?????????? QDataStream sendOut(&outBlock,QIODevice::WriteOnly); ?????????? sendOut.setVersion(QDataStream::Qt_5_8); ?????????? QString currentFileName = fileName.right(fileName.size() ?????? - fileName.lastIndexOf('/')-1); ?????????? //依次写入总大小信息空间,文件名大小信息空间,文件名 ?????????? sendOut << qint64(0) << qint64(0) << currentFileName; ?????????? //这里的总大小是文件名大小等信息和实际文件大小的总和 ?????????? totalBytes += outBlock.size(); ?????????? sendOut.device()->seek(0); ?????????? //返回outBolock的开始,用实际的大小信息代替两个qint64(0)空间 ?????????? sendOut<<totalBytes<<qint64((outBlock.size() - sizeof(qint64)*2)); ?????????? //发送完头数据后剩余数据的大小 ?????????? bytesToWrite = totalBytes - tcpSocket->write(outBlock); ?????????? ui->statuslab->setText(tr("开始发送")); ?????????? outBlock.resize(0); ???? } } (5)发送文件数据。 void Widget::continueSend(qint64 numBytes) { ??? //已经发送数据的大小 ??????? bytesWritten += (int)numBytes; ??????? if(bytesToWrite > 0) //如果已经发送了数据 ??????? { ?????? //每次发送loadSize大小的数据,这里设置为4KB,如果剩余的数据不足4KB, ?????? //就发送剩余数据的大小 ?????????? outBlock = localFile->read(qMin(bytesToWrite,loadSize)); ?????????? //发送完一次数据后还剩余数据的大小 ?????????? bytesToWrite -= (int)tcpSocket->write(outBlock); ?????????? //清空发送缓冲区 ?????????? outBlock.resize(0); ??????? } else { ?????????? localFile->close(); //如果没有发送任何数据,则关闭文件 ??????? } ??????? //更新进度条 ??????? ui->progressBar->setMaximum(totalBytes); ??????? ui->progressBar->setValue(bytesWritten); ??????? if(bytesWritten == totalBytes) //发送完毕 ??????? { ???????? ui->statuslab->setText(tr("传送文件 %1 成功").arg(fileName)); ?????????? localFile->close(); ??????? } } (6)接收数据并显示。 void Widget::readData() { ??? QDataStream in(tcpSocket); ??? in.setVersion(QDataStream::Qt_5_8); ??? if(bytesReceived <= sizeof(qint64)*2) ????? { //如果接收到的数据小于16个字节,那么是刚开始接收数据,我们保存到//来的头文件信息 ???????? if((tcpSocket->bytesAvailable() >= sizeof(qint64)*2) ?????????????? && (fileNameSize == 0)) ?????????? { //接收数据总大小信息和文件名大小信息 ?????????????? in >> totalBytes >> fileNameSize; ?????????????? bytesReceived += sizeof(qint64) * 2; ?????????? } ?????????? if((tcpSocket->bytesAvailable() >= fileNameSize) ?????????????? && (fileNameSize != 0)) ?????????? {? //接收文件名,并建立文件 ?????????????? in >> fileName; ????????????? ui->statuslab->setText(tr("接收文件 %1 ...").arg(fileName)); ?????????????? bytesReceived += fileNameSize; ?????????????? localFile= new QFile(fileName); ?????????????? if(!localFile->open(QFile::WriteOnly)) ?????????????? { ??????????????????? qDebug() << "open file error!"; ????????? ??????????return; ?????????????? } ?????????? } ?????????? else return; ?????? } ?????? if(bytesReceived < totalBytes) ?????? {? //如果接收的数据小于总数据,那么写入文件 ????????? bytesReceived += tcpSocket->bytesAvailable(); ????????? inBlock+= tcpSocket->readAll(); ????? ?} ??? //更新进度条 ?????? ui->progressBar->setMaximum(totalBytes); ?????? ui->progressBar->setValue(bytesReceived); ?????? if(bytesReceived == totalBytes) ?????? { //接收数据完成时 ?????????? //接收显示 ?????????? QBuffer buffer(&inBlock); ?????????? buffer.open(QIODevice::ReadOnly); ?????????? QImageReader reader(&buffer,"jpg"); ?????????? QImage image = reader.read(); ?????????? if(!image.isNull()) ?????????? { ?????????????? image=image.scaled(ui->recLab->size()); ?????????????? ui->recLab->setPixmap(QPixmap::fromImage(image)); ?????????? } ??????? localFile->write(inBlock); ??????? localFile->close(); ??????? inBlock.resize(0); ??????? //重新置0 准备下次接收 ??????? totalBytes = 0; ??????? bytesReceived = 0; ??????? fileNameSize = 0; ??? ui->statuslab->setText(tr("接收文件 %1 成功!").arg(fileName)); ?????? } } 源码下载: |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年4日历 | -2025/4/5 0:15:22- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |