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);
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript