在使用QT 封装好的UDP时候,总感觉不好用,今天我封装了一个win UDP
下面是函数声明
#ifndef WINUDP_H
#define WINUDP_H
#include <winsock2.h> // Windows 套接字 API
#include <ws2tcpip.h> // 提供附加的 IP 地址转换功能
#include <QHostAddress> // Qt 提供的 IP 地址封装类
#include <QObject> // Qt 的基本对象类,支持信号和槽机制
#include <QThread> // Qt 的线程类
// WinUdp 类,继承自 QObject,提供信号和槽机制,用于封装 Windows 下的 UDP 通信
class WinUdp : public QObject
{
Q_OBJECT // Qt 宏,启用信号和槽功能
public:
// 构造函数,初始化 Winsock
WinUdp();
// 析构函数,清理资源
~WinUdp();
// 发送 UDP 消息函数
void writeUdpMessage(const QByteArray &datagram, const QHostAddress &host, quint16 port);
// 返回当前 UDP 套接字缓冲区内可读数据的大小
u_long readUdpDataSize();
// 接收 UDP 数据报函数
qint64 readDatagram(char *data, qint64 maxlen);
// 绑定本地地址和端口函数
bool udpBind(const QHostAddress &address, quint16 port);
// 检查是否有数据可读,并发出信号
void checkRead();
signals:
// 当有数据可读时发出该信号,通知上层应用
void readReady();
private:
// UDP 套接字,Windows 系统中的 SOCKET 类型
SOCKET udpSocket = INVALID_SOCKET;
// 接收数据的地址结构,用于存储接收到的发送方地址
sockaddr_in recvAddr;
// 用于执行接收操作的线程
QThread *receiverThread;
};
#endif // WINUDP_H
下面是函数实现
#include "WinUdp.h"
// 构造函数:用于初始化 Winsock 库,创建网络通信环境
WinUdp::WinUdp()
{
// 初始化 Winsock 库
WSADATA wsaData;
// 使用 MAKEWORD(2, 2) 指定 Winsock 版本 2.2
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
// 检查初始化结果,如果失败直接返回
if (iResult != 0) {
return;
}
}
// 析构函数:用于清理资源,关闭 UDP 套接字,并释放 Winsock 资源
WinUdp::~WinUdp()
{
// 如果 UDP 套接字有效,关闭它
if (udpSocket != INVALID_SOCKET) {
closesocket(udpSocket);
}
// 清理 Winsock 库
WSACleanup();
}
// 发送 UDP 消息
void WinUdp::writeUdpMessage(const QByteArray &datagram, const QHostAddress &host, quint16 port)
{
// 检查 UDP 套接字是否有效
if (udpSocket == INVALID_SOCKET) {
return;
}
// 设置目标地址信息
sockaddr_in destAddr;
destAddr.sin_family = AF_INET; // 使用 IPv4 地址族
destAddr.sin_port = htons(port); // 将端口号转换为网络字节序
destAddr.sin_addr.s_addr = inet_addr(host.toString().toStdString().c_str()); // 将 IP 地址转换为网络格式
// 通过 sendto 发送数据包
int sendResult = sendto(udpSocket, datagram.data(), datagram.size(), 0, (SOCKADDR*)&destAddr, sizeof(destAddr));
if (sendResult == SOCKET_ERROR) {
// 错误处理逻辑,可以显示错误信息或日志记录
// qDebug() << "sendto failed: " << WSAGetLastError();
} else {
// 发送成功后的处理逻辑
// qDebug() << "Data sent successfully.";
}
}
// 获取当前 UDP 套接字可读取的数据字节数
u_long WinUdp::readUdpDataSize()
{
u_long bytesAvailable = 0;
// 使用 ioctlsocket 查询缓冲区中可读取的数据字节数
ioctlsocket(udpSocket, FIONREAD, &bytesAvailable);
return bytesAvailable;
}
// 接收 UDP 数据报
qint64 WinUdp::readDatagram(char *data, qint64 maxlen)
{
// 定义发送方的地址结构
sockaddr_in senderAddr;
int senderAddrSize = sizeof(senderAddr);
// 调用 recvfrom 接收数据,并保存发送方地址
int recvResult = recvfrom(udpSocket, data, maxlen, 0, (SOCKADDR*)&senderAddr, &senderAddrSize);
// 检查接收是否成功
if (recvResult == SOCKET_ERROR)
{
qDebug() << "recvfrom failed, error:" << WSAGetLastError();
return -1; // 返回 -1 表示接收失败
}
// 确保接收到的数据以空字符结尾(适用于字符串)
data[recvResult] = '\0';
// 返回接收到的字节数
return recvResult;
}
// 检查是否有数据可读,并发出信号
void WinUdp::checkRead()
{
while (1)
{
// 检查是否有数据可读
if(readUdpDataSize())
{
// 如果有数据,发出信号通知
emit readReady();
}
}
}
// 绑定 UDP 套接字到指定地址和端口
bool WinUdp::udpBind(const QHostAddress &address, quint16 port)
{
// 创建 UDP 套接字
udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udpSocket == INVALID_SOCKET) {
qDebug() << "Socket creation failed: " << WSAGetLastError();
return false;
}
// 配置本地地址信息
sockaddr_in localAddr;
localAddr.sin_family = AF_INET; // 使用 IPv4
localAddr.sin_port = htons(port); // 将端口号转换为网络字节序
localAddr.sin_addr.s_addr = inet_addr(address.toString().toStdString().c_str()); // 设置本地 IP 地址
// 绑定套接字到指定的 IP 地址和端口
if (bind(udpSocket, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) {
qDebug() << "Bind failed: " << WSAGetLastError();
// 如果绑定失败,关闭套接字并返回 false
closesocket(udpSocket);
return false;
}
// 启动一个接收线程,持续检查是否有数据到达
receiverThread = QThread::create([this]() {
this->checkRead();
});
receiverThread->start();
// 返回 true 表示绑定成功
return true;
}
4223

被折叠的 条评论
为什么被折叠?



