QT 使用Win原始UDP

在使用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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值