Windows Qt调用Vs库实现UDP双口接收数据

代码如下

pro中增加, DEFINES += WIN32_LEAN_AND_MEAN

复制代码
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += WIN32_LEAN_AND_MEAN

DEFINES += QT_DEPRECATED_WARNINGS

h文件

复制代码
#ifndef NETWORKCOMMUNICATION_H
#define NETWORKCOMMUNICATION_H

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <string>
#include <vector>
#include <functional>
#include <thread>
#include <mutex>
#include <atomic>

#pragma comment(lib, "ws2_32.lib")

#include <QObject>
#include <QQueue>
#include <QByteArray>
#include <QTimer>
#include <QElapsedTimer>
#include <QMutexLocker>
#include <QDateTime>

class UDPClientCommunication : public QObject
{
    Q_OBJECT
public:

    explicit UDPClientCommunication(QObject *parent = nullptr);
    ~UDPClientCommunication();

    // 连接设置
    void connectToServer(const QString &address, quint16 port);
    void disconnectFromServer();
    void setLocalPort(quint16 controlPort, quint16 imagePort);

    // 发送数据接口
    void sendData(uint8_t header, const QByteArray &data);

    //响应数据
    void responseData(int msg_type, uint32_t session_id, int value,
                      int total_chunks = 0, int chunk_index = 0);

    // 连接状态
    bool isConnected() const;

signals:
    void handleReceivedControlDataSignals(QByteArray data);
    void handleReceivedImageDataSignals(QByteArray data);

public slots:
    void processControlPendingDatagrams(); // 处理接收数据
    void processImagePendingDatagrams(); // 处理接收数据

private:
    static std::atomic<int> winsockRefCount;
    static std::mutex winsockMutex;
    std::atomic<bool> running;
    std::thread receiveThread;
    std::mutex controlMutex;
    std::mutex imageMutex;

    SOCKET controlSocket;
    SOCKET imageSocket;

    void receiveThreadFunc();

    std::vector<uint8_t> receiveControlBuffer;
    std::vector<uint8_t> receiveImageBuffer;

    // 服务器信息
    QString m_serverAddress;
    quint16 m_serverPort;
    quint16 controlLocalPort;
    quint16 imageLocalPort;

};

#endif // NETWORKCOMMUNICATION_H

cpp文件

复制代码
#include "networkcommunication.h"

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstring>
#include <QString>

std::atomic<int> UDPClientCommunication::winsockRefCount(0);
std::mutex UDPClientCommunication::winsockMutex;

UDPClientCommunication::UDPClientCommunication(QObject *parent)
    : QObject(parent)
{
    m_serverPort = 0;
    imageLocalPort = 0;
    controlLocalPort = 0;

    controlSocket = INVALID_SOCKET;
    imageSocket = INVALID_SOCKET;

    std::lock_guard<std::mutex> lock(winsockMutex);
    if (winsockRefCount == 0) {
        WSADATA wsaData;
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
            qDebug("WSAStartup failed");
            return ;
        }
    }
    winsockRefCount++;
}

UDPClientCommunication::~UDPClientCommunication()
{
    disconnectFromServer();

    std::lock_guard<std::mutex> lock(winsockMutex);
    winsockRefCount--;
    if (winsockRefCount == 0) {
        WSACleanup();
    }
}

void UDPClientCommunication::connectToServer(const QString &address, quint16 port)
{
    m_serverAddress = address;
    m_serverPort = port;

    // 创建控制socket
    controlSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (controlSocket == INVALID_SOCKET) {
        qDebug("Failed to create control socket");
        return;
    }

    // 创建图像socket
    imageSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (imageSocket == INVALID_SOCKET) {
        qDebug("Failed to create image socket");
        closesocket(controlSocket);
        controlSocket = INVALID_SOCKET;
        return;
    }

    // 设置接收缓冲区大小
    int controlBufferSize = 10 * 1024 * 1024;
    setsockopt(controlSocket, SOL_SOCKET, SO_RCVBUF,
        reinterpret_cast<char*>(&controlBufferSize), sizeof(controlBufferSize));

    int imageBufferSize = 50 * 1024 * 1024;
    setsockopt(imageSocket, SOL_SOCKET, SO_RCVBUF,
        reinterpret_cast<char*>(&imageBufferSize), sizeof(imageBufferSize));

    // 绑定控制数据本地端口
    if (controlLocalPort > 0) {
        sockaddr_in controlAddr;
        controlAddr.sin_family = AF_INET;
        controlAddr.sin_addr.s_addr = INADDR_ANY;
        controlAddr.sin_port = htons(controlLocalPort);

        if (bind(controlSocket, (sockaddr*)&controlAddr, sizeof(controlAddr)) == 0) {
            qDebug(QString::fromLocal8Bit("控制数据端口绑定成功, 本地端口号 %1")
                .arg(controlLocalPort).toStdString().c_str());
        }
        else {
            qDebug(QString::fromLocal8Bit("控制数据端口绑定失败, 本地端口号 %1")
                .arg(controlLocalPort).toStdString().c_str());
        }
    }

    // 绑定图像数据本地端口
    if (imageLocalPort > 0) {
        sockaddr_in imageAddr;
        imageAddr.sin_family = AF_INET;
        imageAddr.sin_addr.s_addr = INADDR_ANY;
        imageAddr.sin_port = htons(imageLocalPort);

        if (bind(imageSocket, (sockaddr*)&imageAddr, sizeof(imageAddr)) == 0) {
            qDebug(QString::fromLocal8Bit("图像数据端口绑定成功, 本地端口号 %1")
                .arg(imageLocalPort).toStdString().c_str());
        }
        else {
            qDebug(QString::fromLocal8Bit("图像数据端口绑定失败, 本地端口号 %1")
                .arg(imageLocalPort).toStdString().c_str());
        }
    }

    // 设置为非阻塞模式
    u_long mode = 1;
    ioctlsocket(controlSocket, FIONBIO, &mode);
    ioctlsocket(imageSocket, FIONBIO, &mode);

    // 启动接收线程
    running = true;
    receiveThread = std::thread(&UDPClientCommunication::receiveThreadFunc, this);

    qDebug(QString::fromLocal8Bit("%1:%2 设备连接成功").arg(m_serverAddress).arg(m_serverPort).toStdString().c_str());

}

void UDPClientCommunication::setLocalPort(quint16 controlPort, quint16 imagePort)
{
    controlLocalPort = controlPort;
    imageLocalPort = imagePort;
}

void UDPClientCommunication::disconnectFromServer()
{
    running = false;

    if (receiveThread.joinable()) {
        receiveThread.join();
    }

    if (controlSocket != INVALID_SOCKET) {
        closesocket(controlSocket);
        controlSocket = INVALID_SOCKET;
    }

    if (imageSocket != INVALID_SOCKET) {
        closesocket(imageSocket);
        imageSocket = INVALID_SOCKET;
    }

    qDebug(QString::fromLocal8Bit("%1:%2 设备断开连接")
              .arg(m_serverAddress).arg(m_serverPort).toStdString().c_str());
}

void UDPClientCommunication::sendData(uint8_t header, const QByteArray &data)
{
    if (controlSocket == INVALID_SOCKET) {
        return;
    }

    QByteArray m_sendDataByteArray;

    m_sendDataByteArray.append(static_cast<char>(0x7E));
    m_sendDataByteArray.append(static_cast<char>(0x55));

    // 发送数据
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(m_serverPort);
    inet_pton(AF_INET, m_serverAddress.toStdString().c_str(), &serverAddr.sin_addr);

    int bytesSent = sendto(controlSocket,
        reinterpret_cast<const char*>(m_sendDataByteArray.data()), m_sendDataByteArray.size(), 0,
        (sockaddr*)&serverAddr, sizeof(serverAddr));

    if (bytesSent != -1) {
        QString hexString;
        for(int i = 0; i < bytesSent; i++) {
            hexString += QString("%1 ").arg((uint8_t)m_sendDataByteArray[i], 2, 16, QChar('0')).toUpper();
        }
        qDebug(QString::fromLocal8Bit("发送数据类型: %1 ")
                  .arg(hexString)
                  .toStdString().c_str());
    }
}

void UDPClientCommunication::responseData(int msg_type, uint32_t session_id, int value, int total_chunks, int chunk_index)
{
    // 根据 msg_type 选择使用哪个socket发送响应
    SOCKET socket = controlSocket;
    bool isImageResponse = (msg_type == 0x01);  // 图片数据类型

    if (isImageResponse) {
        socket = imageSocket;
    }

    if (socket == INVALID_SOCKET) {
        return;
    }

    if((msg_type >> 4) == 0xF)
        return;

    QByteArray m_responseData;

    if(msg_type == 0x01) {
        // 构建图片响应
        m_responseData.append(static_cast<char>(0x7E));
        m_responseData.append(static_cast<char>(0x55));
    } else {
        // 构建非图片响应
        m_responseData.append(static_cast<char>(0x7E));
        m_responseData.append(static_cast<char>(0x55));
    }

    // 响应数据
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(m_serverPort);
    inet_pton(AF_INET, m_serverAddress.toStdString().c_str(), &serverAddr.sin_addr);

    int bytesSent = sendto(socket,
        reinterpret_cast<const char*>(m_responseData.data()),
        m_responseData.size(), 0,
        (sockaddr*)&serverAddr, sizeof(serverAddr));

    if (bytesSent != -1) {
        QString hexString;
        for(int i = 0; i < bytesSent; i++) {
            hexString += QString("%1 ").arg((uint8_t)m_responseData[i], 2, 16, QChar('0')).toUpper();
        }
        qDebug(QString::fromLocal8Bit("响应数据类型: %1 长度: %2 内容: %3")
                  .arg(msg_type, 2, 16, QChar('0')).toUpper()
                  .arg(bytesSent)
                  .arg(hexString)
                  .toStdString().c_str());
    }
}

bool UDPClientCommunication::isConnected() const
{
    return (controlSocket != INVALID_SOCKET) || (imageSocket != INVALID_SOCKET);
}

void UDPClientCommunication::receiveThreadFunc()
{
    fd_set readSet;
    timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 10 * 1000;  // 10ms timeout

    while (running) {
        FD_ZERO(&readSet);
        SOCKET maxSocket = INVALID_SOCKET;

        if (controlSocket != INVALID_SOCKET) {
            FD_SET(controlSocket, &readSet);
            maxSocket = controlSocket;
        }
        if (imageSocket != INVALID_SOCKET) {
            FD_SET(imageSocket, &readSet);
            if (imageSocket > maxSocket) maxSocket = imageSocket;
        }

        if (maxSocket == INVALID_SOCKET) {
            Sleep(10);
            continue;
        }

        int selectResult = select(0, &readSet, nullptr, nullptr, &timeout);
        if (selectResult > 0) {
            if (controlSocket != INVALID_SOCKET && FD_ISSET(controlSocket, &readSet)) {
                processControlPendingDatagrams();
            }
            if (imageSocket != INVALID_SOCKET && FD_ISSET(imageSocket, &readSet)) {
                processImagePendingDatagrams();
            }
        }
    }
}

void UDPClientCommunication::processControlPendingDatagrams()
{
    if (controlSocket == INVALID_SOCKET) return;

    char buffer[10*1024] = {0};
    sockaddr_in senderAddr;
    int senderAddrSize = sizeof(senderAddr);

    int bytesReceived = recvfrom(controlSocket, buffer, sizeof(buffer), 0,
        (sockaddr*)&senderAddr, &senderAddrSize);

    if (bytesReceived > 0) {
        std::lock_guard<std::mutex> lock(controlMutex);

        receiveControlBuffer.insert(receiveControlBuffer.end(), buffer, buffer + bytesReceived);

        qint64 m_startTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
        while (receiveControlBuffer.size() > 12) {
            if (receiveControlBuffer[0] != 0x7E || receiveControlBuffer[1] != 0x55) {
                receiveControlBuffer.erase(receiveControlBuffer.begin());
                continue;
            }

            int msg_type = (receiveControlBuffer[2] << 8) | receiveControlBuffer[3];
            uint32_t session_id = (static_cast<uint32_t>(receiveControlBuffer[4]) << 24) |
                (static_cast<uint32_t>(receiveControlBuffer[5]) << 16) |
                (static_cast<uint32_t>(receiveControlBuffer[6]) << 8) |
                static_cast<uint32_t>(receiveControlBuffer[7]);

            int data_size = (receiveControlBuffer[8] << 8) | receiveControlBuffer[9];
            int checksum = (receiveControlBuffer[10] << 8) | receiveControlBuffer[11];

            int totalSize = 12 + data_size;
            if (receiveControlBuffer.size() < totalSize) {
                // 数据不完整,等待更多数据
                qDebug(QString::fromLocal8Bit("[控制]数据不完整: 需要%1字节, 当前%2字节")
                    .arg(totalSize).arg(receiveControlBuffer.size())
                    .toStdString().c_str());
                break;
            }
            //进行数据校验
            uint16_t calcChecksum = 0;
            for (int i = 12; i < totalSize; i++) {
                calcChecksum ^= static_cast<uint8_t>(receiveControlBuffer[i]);
            }

            std::vector<uint8_t> completePacket(receiveControlBuffer.begin(), receiveControlBuffer.begin() + totalSize);
            receiveControlBuffer.erase(receiveControlBuffer.begin(), receiveControlBuffer.begin() + totalSize);

            if (calcChecksum == checksum) {
                responseData(msg_type, session_id, 0);
                QByteArray byteArray(reinterpret_cast<const char*>(completePacket.data()), static_cast<int>(completePacket.size()));
                emit handleReceivedControlDataSignals(byteArray);
            }
            else {
                responseData(msg_type, session_id, 1);
            }
        }
        qint64 m_stopTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
        qDebug(QString::fromLocal8Bit("[控制]处理数据数据时长: %1 ms")
            .arg(QString::number(m_stopTime - m_startTime)).toStdString().c_str());

    }
}

void UDPClientCommunication::processImagePendingDatagrams()
{
    if (imageSocket == INVALID_SOCKET) return;

    char buffer[50*1024] = {0};
    sockaddr_in senderAddr;
    int senderAddrSize = sizeof(senderAddr);

    int bytesReceived = recvfrom(imageSocket, buffer, sizeof(buffer), 0,
        (sockaddr*)&senderAddr, &senderAddrSize);

    if (bytesReceived > 0) {
        std::lock_guard<std::mutex> lock(imageMutex);

        receiveImageBuffer.insert(receiveImageBuffer.end(), buffer, buffer + bytesReceived);

        // 处理接收到的数据
        qint64 m_startTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
        while (receiveImageBuffer.size() > 16) {
            if (receiveImageBuffer[0] != 0x7E || receiveImageBuffer[1] != 0x55) {
                receiveImageBuffer.erase(receiveImageBuffer.begin());
                break;
            }

            int msg_type = (receiveImageBuffer[2] << 8) | receiveImageBuffer[3];
            uint32_t session_id = (static_cast<uint32_t>(receiveImageBuffer[4]) << 24) |
                (static_cast<uint32_t>(receiveImageBuffer[5]) << 16) |
                (static_cast<uint32_t>(receiveImageBuffer[6]) << 8) |
                static_cast<uint32_t>(receiveImageBuffer[7]);

            int total_chunks = (receiveImageBuffer[8] << 8) | receiveImageBuffer[9];
            int chunk_index = (receiveImageBuffer[10] << 8) | receiveImageBuffer[11];
            int data_size = (receiveImageBuffer[12] << 8) | receiveImageBuffer[13];
            int checksum = (receiveImageBuffer[14] << 8) | receiveImageBuffer[15];

            int totalSize = 16 + data_size;
            if (receiveImageBuffer.size() < totalSize) {
                // 数据不完整,等待更多数据
                qDebug(QString::fromLocal8Bit("[图像]数据不完整: 需要%1字节, 当前%2字节")
                    .arg(totalSize).arg(receiveImageBuffer.size())
                    .toStdString().c_str());
                break;
            }

            uint16_t calcChecksum = 0;
            for (int i = 16; i < totalSize; i++) {
                calcChecksum ^= static_cast<uint8_t>(receiveImageBuffer[i]);
            }

            std::vector<uint8_t> completePacket(receiveImageBuffer.begin(), receiveImageBuffer.begin() + totalSize);
            receiveImageBuffer.erase(receiveImageBuffer.begin(), receiveImageBuffer.begin() + totalSize);

            if (calcChecksum == checksum) {
                responseData(msg_type, session_id, 0, total_chunks, chunk_index);
                QByteArray byteArray(reinterpret_cast<const char*>(completePacket.data()), static_cast<int>(completePacket.size()));
                emit handleReceivedImageDataSignals(byteArray);
            }
            else {
                responseData(msg_type, session_id, 1, total_chunks, chunk_index);
            }
        }

        qint64 m_stopTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
        qDebug(QString::fromLocal8Bit("[图像]处理数据数据时长: %1 ms")
            .arg(QString::number(m_stopTime - m_startTime)).toStdString().c_str());
    }
}

使用方式

复制代码
#include "networkcommunication.h"


    UDPClientCommunication m_udpClientCommunication;

    m_udpClientCommunication.setLocalPort(7777, 8888);
    m_udpClientCommunication.connectToServer("192.168.1.120", 6666);
相关推荐
iiiiyu1 小时前
IO流相关编程题
java·大数据·开发语言·数据结构·数据库·mysql
张忠琳1 小时前
【Go 1.26.4】(Part 8) Go 1.26.4 超深度分析 — context + reflect + errors
开发语言·golang
这个DBA有点耶1 小时前
核心系统的高可用与容灾架构:从主从到两地三中心全面解析
java·开发语言·数据库·sql·mysql·架构·运维开发
张忠琳1 小时前
【Go 1.26.4】(Part 3) Go 1.26.4 超深度分析 — Runtime GC 垃圾收集 (mgc*.go + mbitmap.go)
开发语言·golang
码界索隆2 小时前
Python转Java系列:语法与类型系统
java·开发语言·python
ch.ju2 小时前
Java程序设计(第3版)第四章——编译中的错误:无法覆盖
java·开发语言
阿正的梦工坊2 小时前
【Rust】15-Rust 内存布局、Drop 顺序与 unsafe 边界
开发语言·rust
我认不到你2 小时前
【开源、教程】RAG全流程实现(java+完整代码):第二弹
java·开发语言·人工智能·深度学习·ai·语言模型·开源
AKA__Zas2 小时前
初识多线程plus(2.0)
java·开发语言·学习方法