QT之UDP圖片傳輸

上一篇博客是實現了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則是將編碼的工作丟給了外面,只做轉碼的部分(只做數據接收)。

如果這篇文章,對你有幫助,就點個贊再走吧~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章