代码如下
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);