多线程数据接收、转发、存储、显示,要qt实现

在一段时间内,由a向组播一直发送几类数据包,在这段时间里b通过组播同时接收这几类数据,并且不断的转发向另一个组播、边接收边显示接收的数据源码在界面中,并实时动态的将收的数据保存到本地文件中,数据接收和转发显示、存储要同时进行,用多线程实现

【以下回答由 GPT 生成】

以下是实现该功能的步骤和代码示例:

  1. 创建一个Qt应用程序并添加一个MainWindow窗口
#include <QApplication>
#include <QMainWindow>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QMainWindow mainWindow;
    mainWindow.show();

    return app.exec();
}
  1. 创建一个用于接收数据的线程类:
#include <QThread>

class ReceiverThread : public QThread
{
    Q_OBJECT

public:
    explicit ReceiverThread(QObject *parent = nullptr) : QThread(parent) {}

protected:
    void run() override
    {
        // 在这里实现数据接收逻辑
    }
};
  1. 创建一个用于转发数据的线程类:
#include <QThread>

class ForwarderThread : public QThread
{
    Q_OBJECT

public:
    explicit ForwarderThread(QObject *parent = nullptr) : QThread(parent) {}

protected:
    void run() override
    {
        // 在这里实现数据转发逻辑
    }
};
  1. 创建一个用于保存数据的线程类:
#include <QThread>

class SaverThread : public QThread
{
    Q_OBJECT

public:
    explicit SaverThread(QObject *parent = nullptr) : QThread(parent) {}

protected:
    void run() override
    {
        // 在这里实现数据保存逻辑
    }
};
  1. 在MainWindow窗口中创建实例化上述线程类对象并启动线程:
#include <QApplication>
#include <QMainWindow>

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QMainWindow mainWindow;
    mainWindow.show();

    ReceiverThread receiverThread(&mainWindow);
    receiverThread.start();

    ForwarderThread forwarderThread(&mainWindow);
    forwarderThread.start();

    SaverThread saverThread(&mainWindow);
    saverThread.start();

    return app.exec();
}

通过以上步骤和代码示例,你可以实现在Qt中创建一个多线程应用程序,用于接收指定组播数据并实时显示在窗口中,同时将接收到的数据转发至另一个组播中,并将数据边接收边保存到本地文件中。以上示例中的数据接收逻辑、数据转发逻辑和数据保存逻辑需要根据你的具体需求进行实现。



【相关推荐】



如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

一种基于Qt多线程的数据接收方案_qt如何获得线程中的数据_iriczhao的博客-CSDN博客 一、方案背景​很多时候,在进行数据接收时,会面临接收多个数据源的数据,例如:蓝牙设备、RS232通信设备、TCP/IP网络设备。为了统一,常常使目标数据的数据组织格式一样,因此对目标数据的解析方式大多一样。因此可以设计一个多线程的数据接收器,用于接收不同数据源的数据。二、实现思路(1)使QStringList来创建一个全局变量的【数据池】,使用QThread来创建一个读取数据池数据的线程,当读取到一个数据后,便发送“已读取到一个数据“的信号。(2)因数据池在应用程序中,大多情况只存在一个具体实例,_qt如何获得线程中的数据 https://blog.csdn.net/iriczhao/article/details/121503545?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%95%B0%E6%8D%AE%E6%8E%A5%E6%94%B6%E3%80%81%E8%BD%AC%E5%8F%91%E3%80%81%E5%AD%98%E5%82%A8%E3%80%81%E6%98%BE%E7%A4%BA%20%C2%A515%20qt%20c++&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-121503545.142^v92^controlT0_1&spm=1018.2226.3001.4187

参考gpt
以下是一个使用Qt和多线程实现数据接收、转发、存储和显示的示例代码:

#include <QtWidgets>
#include <QUdpSocket>
#include <QThread>

class DataReceiver : public QObject
{
    Q_OBJECT
public:
    explicit DataReceiver(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void receiveData()
    {
        // 创建UDP套接字并绑定到组播地址和端口
        QUdpSocket socket;
        socket.bind(QHostAddress::AnyIPv4, 1234);

        // 加入组播组
        socket.joinMulticastGroup(QHostAddress("224.0.0.1"));

        while (true) {
            QByteArray datagram;
            datagram.resize(socket.pendingDatagramSize());
            socket.readDatagram(datagram.data(), datagram.size());

            // 处理接收到的数据
            // ...

            // 转发数据到另一个组播地址
            socket.writeDatagram(datagram, QHostAddress("224.0.0.2"), 1235);

            // 发送信号通知主线程更新界面
            emit dataReceived(datagram);
        }
    }

signals:
    void dataReceived(const QByteArray &data);
};

class DataStorage : public QObject
{
    Q_OBJECT
public:
    explicit DataStorage(QObject *parent = nullptr) : QObject(parent) {}

public slots:
    void saveData(const QByteArray &data)
    {
        // 将接收到的数据保存到本地文件
        QFile file("data.txt");
        if (file.open(QIODevice::Append)) {
            file.write(data);
            file.close();
        }
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        // 创建界面控件
        QLabel *label = new QLabel(this);
        QTextEdit *textEdit = new QTextEdit(this);

        // 布局界面
        QVBoxLayout *layout = new QVBoxLayout;
        layout->addWidget(label);
        layout->addWidget(textEdit);

        QWidget *centralWidget = new QWidget(this);
        centralWidget->setLayout(layout);
        setCentralWidget(centralWidget);

        // 创建数据接收和存储的对象
        DataReceiver *receiver = new DataReceiver(this);
        DataStorage *storage = new DataStorage(this);

        // 创建接收数据的线程
        QThread *receiverThread = new QThread(this);
        receiver->moveToThread(receiverThread);
        connect(receiverThread, &QThread::started, receiver, &DataReceiver::receiveData);
        connect(receiver, &DataReceiver::dataReceived, this, [=](const QByteArray &data) {
            // 在主线程更新界面
            label->setText(QString("Received: %1").arg(QString(data)));
        });
        connect(receiver, &DataReceiver::dataReceived, storage, &DataStorage::saveData);
        receiverThread->start();
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow mainWindow;
    mainWindow.show();
    return app.exec();
}

#include "main.moc"

该示例代码使用Qt的多线程功能创建了两个线程,一个用于数据接收,另一个用于数据存储。数据接收线程通过UDP套接字接收组播数据,并将接收到的数据转发到另一个组播地址。同时,接收到的数据也会通过信号通知主线程更新界面,并实时将数据保存到本地文件中。

请注意,这只是一个示例代码,具体的实现可能需要根据您的需求进行修改和调整。您可以根据自己的实际情况,调整数据的处理逻辑、界面显示和文件保存方式等。

另外,请确保在使用Qt的多线程功能时,遵循Qt的线程安全规则,例如避免在多个线程中同时访问和修改同一数据。


使用C++实现UDP接收、转发和存储的完整示例代码:

cpp
Copy Code
#include <iostream>
#include <fstream>
#include <string>
#include <thread>
#include <chrono>
#include <cstring>

#ifdef _WIN32
    #include <winsock2.h>
    #pragma comment(lib, "ws2_32.lib")
#else
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
#endif

const int BUFFER_SIZE = 1024; // 接收缓冲区大小

// 接收数据线程函数
void receiveData()
{
    // 创建UDP socket
    int udpSocket;
#ifdef _WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        std::cerr << "Failed to initialize Winsock" << std::endl;
        return;
    }
    udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
#else
    udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
#endif

    // 设置接收端口和IP地址
    int port = 12345; // 设置UDP接收端口
    std::string ip = "127.0.0.1"; // 设置组播IP地址
    struct sockaddr_in localAddress;
    localAddress.sin_family = AF_INET;
    localAddress.sin_port = htons(port);
    localAddress.sin_addr.s_addr = inet_addr(ip.c_str());

    // 将socket绑定到本地地址
    if (bind(udpSocket, (struct sockaddr*)&localAddress, sizeof(localAddress)) == -1)
    {
        std::cerr << "Failed to bind socket" << std::endl;
#ifdef _WIN32
        closesocket(udpSocket);
#else
        close(udpSocket);
#endif
        return;
    }

    // 接收数据并转发
    char buffer[BUFFER_SIZE];
    while (true)
    {
        // 接收UDP数据包
        struct sockaddr_in clientAddress;
        socklen_t clientAddressLength = sizeof(clientAddress);
        memset(buffer, 0, BUFFER_SIZE); // 清空接收缓冲区
        int bytesRead = recvfrom(udpSocket, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&clientAddress, &clientAddressLength);
        if (bytesRead <= 0)
        {
            std::cerr << "Failed to receive data" << std::endl;
            continue;
        }

        // 将接收的数据源码在窗口中显示
        std::cout << "Received Data: " << buffer << std::endl;

        // 转发至另一个组播
        int forwardPort = 23456; // 设置转发的组播端口
        std::string forwardIp = "127.0.0.1"; // 设置转发的组播IP地址
        struct sockaddr_in forwardAddress;
        forwardAddress.sin_family = AF_INET;
        forwardAddress.sin_port = htons(forwardPort);
        forwardAddress.sin_addr.s_addr = inet_addr(forwardIp.c_str());
        if (sendto(udpSocket, buffer, bytesRead, 0, (struct sockaddr*)&forwardAddress, sizeof(forwardAddress)) == -1)
        {
            std::cerr << "Failed to forward data" << std::endl;
            continue;
        }

        // 将接收的数据保存到本地文件
        std::ofstream file("received_data.txt", std::ios::app);
        if (file.is_open())
        {
            file << buffer << std::endl;
            file.close();
        }

        // 等待一段时间再继续接收数据
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    // 关闭UDP socket
#ifdef _WIN32
    closesocket(udpSocket);
    WSACleanup();
#else
    close(udpSocket);
#endif
}

int main()
{
    // 创建接收数据的线程
    std::thread receiveThread(receiveData);

    // 主线程执行其他任务

    // 等待问题结题日期
    std::this_thread::sleep_until(std::chrono::system_clock::time_point(std::chrono::seconds(1669059200)));  // 结题日期为2022-08-22 00:00:00

    // 结束接收数据线程
    receiveThread.join();

    return 0;
}
注意,在Windows平台上,需要链接ws2_32.lib库文件(在代码中使用了#pragma comment(lib, "ws2_32.lib")),并在程序开始前调用WSAStartup函数进行Winsock库的初始化,并在程序结束后调用WSACleanup函数释放资源

img

下面这是基于java实现的题主思路,题主根据自己的ip和请求更改接口定义可以进行实现,希望可以帮到您


import java.io.FileOutputStream;
import java.io.IOException;
import java.net.*;

public class MulticastReceiver {
    public static void main(String[] args) {
        // 设置接收组播数据的地址和端口
        String multicastAddress = "224.0.0.1";
        int multicastPort = 8888;

        // 设置转发组播数据的地址和端口
        String forwardAddress = "224.0.0.2";
        int forwardPort = 9999;

        // 创建接收组播数据的线程
        Thread receiverThread = new Thread(() -> {
            try {
                InetAddress group = InetAddress.getByName(multicastAddress);
                MulticastSocket multicastSocket = new MulticastSocket(multicastPort);
                multicastSocket.joinGroup(group);

                byte[] buffer = new byte[1024];
                while (true) {
                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                    multicastSocket.receive(packet);
                    String receivedData = new String(packet.getData(), 0, packet.getLength());

                    // 在窗口中显示接收到的数据
                    System.out.println("Received from " + packet.getAddress() + ": " + receivedData);

                    // 转发数据至另一个组播
                    InetAddress forwardGroup = InetAddress.getByName(forwardAddress);
                    DatagramPacket forwardPacket = new DatagramPacket(packet.getData(), packet.getLength(), forwardGroup, forwardPort);
                    
                    // 创建发送组播数据的socket
                    MulticastSocket forwardSocket = new MulticastSocket();
                    forwardSocket.setTimeToLive(1); // 设置TTL为1,确保数据只在本地网络内传播
                    forwardSocket.send(forwardPacket);
                    forwardSocket.close();

                    // 将接收的数据保存到本地文件
                    try (FileOutputStream fos = new FileOutputStream("received_data.txt", true)) {
                        fos.write(receivedData.getBytes());
                        fos.write("\n".getBytes());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        // 启动接收组播数据的线程
        receiverThread.start();
    }
}

引用chatgpt内容作答:
你想实现一个多线程的程序,该程序能够同时进行以下操作:

1、以UDP方式接收指定源的组播数据。
2、在窗口中实时显示接收到的数据。
3、将接收到的数据转发到另一个组播地址。
4、边接收边将数据保存到本地文件中。
以下是一个Python代码示例,用于实现你所描述的功能。请注意,这只是一个简单的示例,实际场景中可能需要根据具体需求进行更详细的调整和优化。

import socket
import threading
import time

# UDP接收和转发的组播地址和端口
receive_multicast_group = ('224.0.0.1', 5000)
forward_multicast_group = ('224.0.0.2', 5001)

# 本地保存数据的文件名
output_file = 'received_data.txt'

# 创建UDP接收套接字
receive_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
receive_socket.bind(('', receive_multicast_group[1]))
receive_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(receive_multicast_group[0]) + socket.inet_aton('0.0.0.0'))

# 创建UDP转发套接字
forward_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 创建窗口显示接收到的数据
def display_received_data():
    while True:
        try:
            data, addr = receive_socket.recvfrom(1024)
            print(f"Received: {data.decode('utf-8')} from {addr}")
        except Exception as e:
            print(f"Error in receiving data: {e}")

# 创建线程用于显示接收到的数据
display_thread = threading.Thread(target=display_received_data)
display_thread.start()

# 创建线程用于转发接收到的数据
def forward_received_data():
    while True:
        try:
            data, addr = receive_socket.recvfrom(1024)
            forward_socket.sendto(data, forward_multicast_group)
        except Exception as e:
            print(f"Error in forwarding data: {e}")

forward_thread = threading.Thread(target=forward_received_data)
forward_thread.start()

# 创建线程用于保存接收到的数据到本地文件
def save_received_data():
    while True:
        try:
            data, _ = receive_socket.recvfrom(1024)
            with open(output_file, 'a') as f:
                f.write(f"{time.ctime()}: {data.decode('utf-8')}\n")
        except Exception as e:
            print(f"Error in saving data: {e}")

save_thread = threading.Thread(target=save_received_data)
save_thread.start()

# 主线程等待所有子线程完成
try:
    display_thread.join()
    forward_thread.join()
    save_thread.join()
except KeyboardInterrupt:
    print("Program terminated by user.")

# 关闭套接字
receive_socket.close()
forward_socket.close()

在这个示例中,我们创建了三个线程来分别处理数据的显示、转发和保存。每个线程都有一个死循环来持续执行相应的操作。请注意,这只是一个基本的框架,你可能需要根据具体需求进行更多的调整,例如添加适当的异常处理、退出机制以及线程间的同步等。

另外,请确保你的操作系统和网络环境允许组播通信,并根据实际情况调整组播地址和端口号。此外,记得根据需要修改代码,使其适应实际业务逻辑。


import socket
import threading

# 设置参数
source_group = "224.0.0.1"  # 指定源组播地址
source_port = 5000          # 源组播端口
dest_group = "224.0.0.2"    # 目标组播地址
dest_port = 6000            # 目标组播端口
buffer_size = 1024          # 缓冲区大小
output_file = "received_data.txt"  # 保存接收数据的文件名

# 创建源组播套接字
source_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
source_sock.bind(('', source_port))
source_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(source_group) + socket.inet_aton("0.0.0.0"))

# 创建目标组播套接字
dest_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
dest_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
dest_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
dest_address = (dest_group, dest_port)

# 创建窗口显示接收的数据
def display_received_data():
    while True:
        data, _ = source_sock.recvfrom(buffer_size)
        print("Received:", data.decode())

# 启动窗口显示线程
display_thread = threading.Thread(target=display_received_data)
display_thread.start()

# 接收数据并转发至目标组播和保存到文件
while True:
    data, addr = source_sock.recvfrom(buffer_size)
    print("Received:", data.decode())

    # 转发数据至目标组播
    dest_sock.sendto(data, dest_address)

    # 保存数据到文件
    with open(output_file, 'a') as f:
        f.write(data.decode() + '\n')

可以参考下,ip需要更改

#include <QtWidgets>
#include <QUdpSocket>
#include <QFile>
#include <QTextStream>
#include <QThread>

class UdpReceiver : public QObject
{
    Q_OBJECT
public:
    explicit UdpReceiver(QObject* parent = nullptr) : QObject(parent)
    {
        udpSocket = new QUdpSocket(this);
        udpSocket->bind(QHostAddress::AnyIPv4, 12345, QUdpSocket::ShareAddress);

        connect(udpSocket, &QUdpSocket::readyRead, this, &UdpReceiver::processPendingDatagrams);
    }

signals:
    void dataReceived(const QByteArray& data);

private slots:
    void processPendingDatagrams()
    {
        while (udpSocket->hasPendingDatagrams())
        {
            QByteArray datagram;
            datagram.resize(udpSocket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;

            udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

            emit dataReceived(datagram);
        }
    }

private:
    QUdpSocket* udpSocket;
};

class UdpForwarder : public QObject
{
    Q_OBJECT
public:
    explicit UdpForwarder(QObject* parent = nullptr) : QObject(parent)
    {
        udpSocket = new QUdpSocket(this);
        udpSocket->bind(QHostAddress::AnyIPv4, 23456, QUdpSocket::ShareAddress);
    }

public slots:
    void forwardData(const QByteArray& data)
    {
        udpSocket->writeDatagram(data, QHostAddress("196.168.0.1"), 12345);
    }

private:
    QUdpSocket* udpSocket;
};

class DataProcessor : public QObject
{
    Q_OBJECT
public:
    explicit DataProcessor(QObject* parent = nullptr) : QObject(parent) {}

public slots:
    void processData(const QByteArray& data)
    {
        // 在这里进行数据处理
        // ...

        // 发送显示接收的数据信号
        emit displayData(data);

        // 发送转发数据信号
        emit forwardData(data);

        // 发送保存数据到本地文件信号
        emit saveData(data);
    }

signals:
    void displayData(const QByteArray& data);
    void forwardData(const QByteArray& data);
    void saveData(const QByteArray& data);
};

class ReceiverThread : public QThread
{
    Q_OBJECT
public:
    explicit ReceiverThread(QObject* parent = nullptr) : QThread(parent)
    {
        receiver = new UdpReceiver();
        processor = new DataProcessor();
        forwarder = new UdpForwarder();
        connect(receiver, &UdpReceiver::dataReceived, processor, &DataProcessor::processData);
        connect(processor, &DataProcessor::displayData, this, &ReceiverThread::displayData);
        connect(processor, &DataProcessor::forwardData, forwarder, &UdpForwarder::forwardData);
        connect(processor, &DataProcessor::saveData, this, &ReceiverThread::saveData);
    }

signals:
    void displayData(const QByteArray& data);

protected:
    void run() override
    {
        exec();
    }

private:
    UdpReceiver* receiver;
    UdpForwarder* forwarder;
    DataProcessor* processor;
    QFile file;

private slots:
    void displayData(const QByteArray& data)
    {
        emit this->displayData(data);
    }

    void saveData(const QByteArray& data)
    {
        if (!file.isOpen())
        {
            QString fileName = QCoreApplication::applicationDirPath() + "/received_data.txt";
            file.setFileName(fileName);
            if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
            {
                qWarning() << "Failed to open file for writing";
                return;
            }
        }

        QTextStream stream(&file);
        stream << QString(data) << endl;
    }
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget* parent = nullptr) : QMainWindow(parent)
    {
        receiverThread = new ReceiverThread(this);
        connect(receiverThread, &ReceiverThread::displayData, this, &MainWindow::appendToLog);

        startButton = new QPushButton("Start", this);
        connect(startButton, &QPushButton::clicked, this, &MainWindow::startReceiverThread);

        logTextEdit = new QTextEdit(this);
        logTextEdit->setReadOnly(true);

        QVBoxLayout* layout = new QVBoxLayout();
        layout->addWidget(startButton);
        layout->addWidget(logTextEdit);

        QWidget* centralWidget = new QWidget(this);
        centralWidget->setLayout(layout);
        setCentralWidget(centralWidget);
    }

private slots:
    void appendToLog(const QByteArray& data)
    {
        logTextEdit->append(QString(data));
    }

    void startReceiverThread()
    {
        receiverThread->start();
    }
private:
    ReceiverThread *receiverThread;
    QPushButton* startButton;
    QTextEdit* logTextEdit;
};

引用chatgpt



#include <QtWidgets>
#include <QUdpSocket>
#include <QThread>

class ReceiverThread : public QThread
{
    Q_OBJECT
public:
    explicit ReceiverThread(QObject *parent = nullptr) : QThread(parent) {}

    void run() override
    {
        QUdpSocket socket;
        socket.bind(QHostAddress::AnyIPv4, 1234);

        while (!isInterruptionRequested()) {
            QByteArray datagram;
            datagram.resize(socket.pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;

            socket.readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

            // 处理接收到的数据
            emit dataReceived(sender.toString(), datagram);

            // 转发数据
            forwardSocket.writeDatagram(datagram, QHostAddress("224.0.0.2"), 1235);

            // 保存数据到本地文件
            saveFile.write(datagram);
        }
    }

signals:
    void dataReceived(const QString &sender, const QByteArray &data);

public:
    QUdpSocket forwardSocket;
    QFile saveFile;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr) : QMainWindow(parent)
    {
        receiverThread.forwardSocket.bind(QHostAddress::AnyIPv4, 1235);

        connect(&receiverThread, &ReceiverThread::dataReceived, this, &MainWindow::onDataReceived);

        receiverThread.saveFile.setFileName("data.txt");
        receiverThread.saveFile.open(QIODevice::WriteOnly | QIODevice::Append);

        receiverThread.start();
    }

    ~MainWindow() override
    {
        receiverThread.quit();
        receiverThread.wait();
    }

private slots:
    void onDataReceived(const QString &sender, const QByteArray &data)
    {
        // 在窗口中显示接收到的数据
        QTextEdit *textEdit = new QTextEdit;
        textEdit->setWindowTitle("Received from: " + sender);
        textEdit->setPlainText(QString::fromUtf8(data));
        textEdit->show();
    }

private:
    ReceiverThread receiverThread;
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MainWindow mainWindow;
    mainWindow.show();

    return app.exec();
}




需要详细代码请评论


#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include <QUdpSocket>
#include <QFile>

// 数据接收线程
class ReceiverThread : public QThread
{
    Q_OBJECT
public:
    explicit ReceiverThread(QObject *parent = nullptr) : QThread(parent) {}

signals:
    void dataReceived(const QByteArray &data);

protected:
    void run() override {
        // 创建UDP套接字
        QUdpSocket socket;
        socket.bind(QHostAddress::AnyIPv4, 1234); // 绑定接收端口

        while (!isInterruptionRequested()) {
            // 接收数据
            QByteArray data;
            data.resize(socket.pendingDatagramSize());
            socket.readDatagram(data.data(), data.size());

            // 发送数据接收信号
            emit dataReceived(data);
        }
    }

可以使用Qt的QThread类来创建多个线程,分别用于数据接收、转发、存储和显示。你的需要用过需要接收、转发和存储三个线程。
可以参考:
https://blog.csdn.net/qq_43341440/article/details/118182567
或者:https://blog.csdn.net/m0_60259116/article/details/130566630

我们创建了一个多线程的UDP接收程序。UdpReceiver 类用于在一个独立的线程中进行UDP数据接收、显示、转发和保存操作,而 ReceiverThread 类则继承自 QThread,用于创建并管理接收线程。

主要流程如下:

  1. UdpReceiver 类继承自 QObject,在构造函数中初始化UDP接收和发送套接字。通过连接 readyRead 信号来读取接收到的数据,然后将数据显示在窗口中、转发到另一个组播地址,并保存到文件中。

  2. ReceiverThread 类继承自 QThread,在 run 函数中创建 UdpReceiver 实例并启动事件循环。

  3. 在 main 函数中,创建一个 QTextEdit 窗口用于显示接收到的数据。然后创建一个 ReceiverThread 实例,连接 newDataReceived 信号到窗口的 append 槽,从而将接收到的数据显示在窗口中。

以下是一个使用Qt和C++实现多线程UDP接收、显示、转发和保存组播数据的示例代码:

#include <QtWidgets/QApplication>
#include <QtWidgets/QTextEdit>
#include <QUdpSocket>
#include <QThread>
#include <QFile>
#include <QTextStream>
#include <QDebug>

class UdpReceiver : public QObject {
    Q_OBJECT
public:
    UdpReceiver(QObject* parent = nullptr) : QObject(parent) {
        udpSocket.bind(QHostAddress::Any, RECV_PORT, QUdpSocket::ShareAddress);
        udpSocket.joinMulticastGroup(QHostAddress(MULTICAST_GROUP));

        connect(&udpSocket, &QUdpSocket::readyRead, this, &UdpReceiver::readPendingDatagrams);
    }

signals:
    void newDataReceived(const QString& data);

private slots:
    void readPendingDatagrams() {
        while (udpSocket.hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(udpSocket.pendingDatagramSize());
            QHostAddress sender;
            udpSocket.readDatagram(datagram.data(), datagram.size(), &sender);

            QString dataString = QString::fromUtf8(datagram);
            emit newDataReceived(dataString);

            sendSocket.writeDatagram(datagram, QHostAddress(SEND_MULTICAST_GROUP), SEND_PORT);

            saveToFile(dataString);
        }
    }

    void saveToFile(const QString& data) {
        QFile file("received_data.txt");
        if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
            QTextStream stream(&file);
            stream << data << "\n";
        }
    }

private:
    QUdpSocket udpSocket;
    QUdpSocket sendSocket;
    const QString MULTICAST_GROUP = "224.0.0.1";
    const quint16 RECV_PORT = 5000;
    const QString SEND_MULTICAST_GROUP = "224.0.0.2";
    const quint16 SEND_PORT = 6000;
};

class ReceiverThread : public QThread {
    Q_OBJECT
public:
    ReceiverThread(QObject* parent = nullptr) : QThread(parent) {}

protected:
    void run() override {
        UdpReceiver receiver;
        connect(&receiver, &UdpReceiver::newDataReceived, this, &ReceiverThread::handleNewData);
        exec();
    }

signals:
    void newDataReceived(const QString& data);

private slots:
    void handleNewData(const QString& data) {
        emit newDataReceived(data);
    }
};

int main(int argc, char* argv[]) {
    QApplication a(argc, argv);

    QTextEdit textEdit;
    textEdit.setWindowTitle("UDP Receiver");
    textEdit.resize(400, 300);
    textEdit.show();

    ReceiverThread receiverThread;
    receiverThread.start();

    QObject::connect(&receiverThread, &ReceiverThread::newDataReceived, [&textEdit](const QString& data) {
        textEdit.append(data);
    });

    return a.exec();
}

如果有问题,请提出;没有问题请采纳。

通常写代码的时候QObject派生类对象的创建是在主线程中,所以connect连接信号槽默认走得是主线程的事件循环,信号触发后槽函数会在主线程中执行槽函数,这样就等于没有跨线程接收。

如果需要在其他线程中执行槽函数,则需要通过在QThread::run()函数中创建对象并且执行QThread::exec()启动线程自己的事件循环;
又或者通过QObject::moveToThread,设定一个QThread对象执行事件循环并将一个自定义的QObject派生类对象强制转移到该线程中。
总而言之就是要在线程自己的事件循环中处理信号和槽函数的连接

QUdpSocket、QTcpSocket内部存在信号槽的连接,如果一个对象在两个拥有自己事件循环的线程中connect信号槽,则可能信号和槽对接不上,所以这两个对象甚至是其他的一些IO类都是不支持跨线程访问的。

但是它们的基类QAbstractSocket提供了QAbstractSocket::socketDescriptor()和QAbstractSocket::setSocketDescriptor()可以用于获取套接字描述符和设置套接字描述符。所以你可以在两个线程外部创建一个QUdpSocket对象处理bind和加入组播组等操作,然后将其套接字描述符传入这两个线程中,再分别创建QUdpSocket对象并设置其套接字描述符,然后就可以实现跨线程的收发了。