上一篇博客是实现了UDP实现文本传输,而这一篇是实现图片传输。代码的话,是在文本传输的例子上进行修改的。
从本质上来说,无论是传输文本,还是传输图片,在传输的过程中实际上都是传输的二进制数据,只不过是它们的表现形式不一样罢了。对于文本而已,就是单纯的ASCII码(也就是一个字节的16进制数),而对于图片这种类型的文件,我们也是可以先进行数据转换,然后将转换后的数据发送到另外一个客户端,另外一个客户端接收到数据,再将其转换为图片的形式显示出来。其实这里面就只是涉及到一个编码和转码的问题而已。知道原理是什么之后,接下来就是用代码去怎么去实现这个编码和转码的事了。
做过图片转换的童鞋来说,对图片base64编码肯定不陌生。网上也提供了很多这样的工具,就是打开一张图片,然后转换为ASCII码的数据,感兴趣的童鞋可以去了解看看。在QT中,也是提供了base64编码的方法供我们使用的,现在就来看看代码怎么实现吧。
UI图如下,主要是在上一个工程简单修改而来的【说实话,确实是丑,但这里不是关键地方哈~~~~】。
相比上一工程,主要增加了4个控件,分别为两个label和两个按键。
核心代码(即编码和转码)如下:
1.将图片数据转换成base64格式
QByteArray MainWindow::get_imagedata_from_imagefile(const QImage &image)
{
QByteArray imageData;
QBuffer buffer(&imageData);
image.save(&buffer, "jpg");
imageData = imageData.toBase64();
return imageData;
}
2.将base64数据转换为图片
QImage MainWindow::get_imagedata_from_byte(const QString &data)
{
QByteArray imageData = QByteArray::fromBase64(data.toLatin1());
QImage image;
image.loadFromData(imageData);
return image;
}
相对于上一个版本主要有以下修改:
1.在mainwindow.h文件中增加以下代码
#include <QImage>//增加头文件
//在类中增加以下变量定义和函数声明
QString image_file_data;
QImage image;
QString picture_data;
QByteArray get_imagedata_from_imagefile(const QImage &image);
QImage get_imagedata_from_byte(const QString &data);
2.修改原来的void MainWindow::udp_server_receive_data()函数
//将原来的文字传输改成图片数据转码
void MainWindow::udp_server_receive_data()
{
QByteArray datagram;
while( udp_server->hasPendingDatagrams())
{
//数据不为空
datagram.resize( udp_server->pendingDatagramSize() );
//接收数据报
udp_server->readDatagram(datagram.data(),datagram.size(), &client_address, &client_port);
/* 文本传输注销
//label->setText(datagram);
QString ip_addr_temp = client_address.toString();//"::ffff:192.168.124.11"
QString ip_addr;
int i = 0;
//填充ip地址
for(i = 7; ip_addr_temp[i] != '\0'; i ++ )
{
ip_addr[i-7] = ip_addr_temp[i];
}
//<receive ip:port>:data
receive_buff += QString("<receive ") + ip_addr + QString(":") + QString::number(client_port) + QString(">:") + datagram.data();
//显示区
ui->receive_buff_textBrowser->setText(receive_buff);
*/
//接收显示的图片
QImage image_temp = get_imagedata_from_byte(datagram.data());
ui->image_label_2->setPixmap(QPixmap::fromImage(image_temp).scaled(ui->image_label_2->size()));
}
}
3.修改发送函数,这里的话是使用了发送图片的控件槽发送的。跟之前使用发送文本的控件槽函数差不多,就是多了一个编码的过程
void MainWindow::on_send_picture_pushButton_clicked()
{
//获取要发送的客户端ip和端口号
image_file_data = get_imagedata_from_imagefile(image);//获取图片数据
qDebug()<<"===========size==================="<<image_file_data.size();
QString client_addr_buff = ui->udp_client_ip_textEdit->document()->toPlainText();
QString client_port_buff = ui->udp_client_port_textEdit->document()->toPlainText();
//发送图片
udp_server->writeDatagram(image_file_data.toLatin1(),image_file_data.size(),QHostAddress(client_addr_buff),client_port_buff.toInt());
}
对比前面一个文本发送的程序,就是改了上述三个关键的函数发送和接收地方。对于还有一些其他的小细节问题,比如打开文件之类的,就请看完整程序吧。
完整代码如下:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>
#include <QImage>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void localhost_ip_get(void);
QUdpSocket *udp_server;
QString receive_buff;
QHostAddress client_address;
quint16 client_port = 0;
//图片显示新增
QString image_file_data;
QImage image;
QString picture_data;
QByteArray get_imagedata_from_imagefile(const QImage &image);
QImage get_imagedata_from_byte(const QString &data);
private:
Ui::MainWindow *ui;
private slots:
void udp_server_receive_data(void);
void on_listen_pushButton_clicked();
void on_send_pushButton_clicked();
void on_clear_send_buff_pushButton_clicked();
void on_clear_receive_buff_pushButton_clicked();
void on_open_picture_pushButton_clicked();
void on_send_picture_pushButton_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QNetworkInterface>
#include <QMessageBox>
#include <QFileDialog>
#include <QBuffer>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//设置标题
this->setWindowTitle("UDP_SERVER");
localhost_ip_get();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::localhost_ip_get()
{
udp_server = new QUdpSocket(this);
ui->udp_server_ip_textEdit->setText(QNetworkInterface().allAddresses().at(1).toString());//获取本地IP
}
void MainWindow::udp_server_receive_data()
{
QByteArray datagram;
while( udp_server->hasPendingDatagrams())
{
//数据不为空
datagram.resize( udp_server->pendingDatagramSize() );
//接收数据报
udp_server->readDatagram(datagram.data(),datagram.size(), &client_address, &client_port);
/*
//label->setText(datagram);
QString ip_addr_temp = client_address.toString();//"::ffff:192.168.124.11"
QString ip_addr;
int i = 0;
//填充ip地址
for(i = 7; ip_addr_temp[i] != '\0'; i ++ )
{
ip_addr[i-7] = ip_addr_temp[i];
}
//<receive ip:port>:data
receive_buff += QString("<receive ") + ip_addr + QString(":") + QString::number(client_port) + QString(">:") + datagram.data();
//显示区
ui->receive_buff_textBrowser->setText(receive_buff);
*/
//接收显示的图片
QImage image_temp = get_imagedata_from_byte(datagram.data());
ui->image_label_2->setPixmap(QPixmap::fromImage(image_temp).scaled(ui->image_label_2->size()));
}
}
void MainWindow::on_listen_pushButton_clicked()
{
//readyRead:每当有数据报来时发送这个信号
if(ui->listen_pushButton->text() == "监听")
{
ui->listen_pushButton->setText("断开");
//获取端口号
QString port_buff = ui->udp_server_port_textEdit->document()->toPlainText();
quint16 port_val = (quint16 )port_buff.toInt();
udp_server->bind(port_val,QUdpSocket::ShareAddress);
connect(udp_server,SIGNAL(readyRead()),this,SLOT(udp_server_receive_data()));
}
else
{
ui->listen_pushButton->setText("监听");
udp_server->close();
}
}
void MainWindow::on_send_pushButton_clicked()
{
//获取填写的数据
QString send_buff = ui->send_buff_textEdit->document()->toPlainText();
//获取要发送的客户端ip和端口号
QString client_addr_buff = ui->udp_client_ip_textEdit->document()->toPlainText();
QString client_port_buff = ui->udp_client_port_textEdit->document()->toPlainText();
if(client_addr_buff == "" || client_port_buff == "")
{
QMessageBox::information(nullptr, "Error", "IP或端口号内容不能为空");
return;
}
if(send_buff == "")
{
QMessageBox::information(nullptr, "Error", "内容不能为空");
return;
}
//udp_server->writeDatagram(send_buff.toLatin1(),send_buff.size(),client_address,client_port);
udp_server->writeDatagram(send_buff.toLatin1(),send_buff.size(),QHostAddress(client_addr_buff),client_port_buff.toInt());
}
void MainWindow::on_clear_send_buff_pushButton_clicked()
{
ui->send_buff_textEdit->clear();
}
void MainWindow::on_clear_receive_buff_pushButton_clicked()
{
ui->receive_buff_textBrowser->clear();
}
void MainWindow::on_open_picture_pushButton_clicked()
{
QString file_name = QFileDialog::getOpenFileName(this,"open file",QDir::currentPath(),"Image Files(*.jpg *.png)");
if(file_name == "")
{
return;
}
image.load(file_name);
ui->image_label->setPixmap(QPixmap::fromImage(image).scaled(ui->image_label->size()));//显示发送的图片
//ui->image_label
}
QByteArray MainWindow::get_imagedata_from_imagefile(const QImage &image)
{
QByteArray imageData;
QBuffer buffer(&imageData);
image.save(&buffer, "jpg");
imageData = imageData.toBase64();
return imageData;
}
QImage MainWindow::get_imagedata_from_byte(const QString &data)
{
QByteArray imageData = QByteArray::fromBase64(data.toLatin1());
QImage image;
image.loadFromData(imageData);
return image;
}
void MainWindow::on_send_picture_pushButton_clicked()
{
//获取要发送的客户端ip和端口号
image_file_data = get_imagedata_from_imagefile(image);//获取图片数据
qDebug()<<"===========size==================="<<image_file_data.size();
QString client_addr_buff = ui->udp_client_ip_textEdit->document()->toPlainText();
QString client_port_buff = ui->udp_client_port_textEdit->document()->toPlainText();
//发送图片
udp_server->writeDatagram(image_file_data.toLatin1(),image_file_data.size(),QHostAddress(client_addr_buff),client_port_buff.toInt());
}
测试结果:(测试图片不能大于80k,否则不能正常显示)
第一种:自发自收
1.运行程序,点击监听,然后服务器IP和端口号拷贝到客户端IP和PORT。
2.打开一张图片,然后点击发送图片。
第二种方法:服务器只负责收,图片数据是别的客户端发送过来的。【主要是让童鞋们更加了解这个编码和转码的过程】
1.这里的话,我们需要将图片转码,图片转码工具。我们打开一张图片,然后转换得到base64编码的内容(注意前面这部分的数据不要),然后将拷贝数据到一个UDP客户端,通过UDP客户端进行发送数据给这里的服务器进行显示。
2.重新运行一次程序,点击监听。然后打开另外一个UDP工具的工具,点击链接QT的服务器,然后将图片数据拷贝发送,这样我们就可以在QT上位机上看到我们的图片了。
方法1和方法2的区别:
方法1是QT上位机里面自己做了转码和编码的工作。而方法2则是将编码的工作丢给了外面,只做转码的部分(只做数据接收)。
如果这篇文章,对你有帮助,就点个赞再走吧~~~~