上一篇博客是實現了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則是將編碼的工作丟給了外面,只做轉碼的部分(只做數據接收)。
如果這篇文章,對你有幫助,就點個贊再走吧~~~~