IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: 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文件,设计界面如下。

其中“服务器端”LabelobjectNamestatuslab;进度条ProgressBarobjectNameprogressBar,设置其value属性为0;发送按钮的objectNamesendButton;图片显示textlabel的objectName为recLab。

效果如下。

步骤3.更改widget.h文件的内容。

  1. 添加头文件包含:#include?<QtNetwork>

2)添加私有变量:

? ?QTcpServer *tcpServer;

??? 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 NewConnection();

??? 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 EditobjectNameipEdit;“端口”后的Line EditobjectNameportEdit;下面的Progress BarobjectNameprogressBar,其value属性设为24;“状态”LabelobjetNamestatusLab;“打开”按钮的objectNameconBun;“发送”按钮的objectNamesendBun
?

步骤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)添加私有槽函数:

private?slots: 
void newConnect(); //连接服务器

??? 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));

?????? }

}

源码下载:

https://download.csdn.net/download/delphi863/20478903

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-27 16:35:43  更:2021-07-27 16:36:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 13:34:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码