Qt中通过C++ 实现udp广播报文

udp广播原理介绍

UDP是面向非连接的网络交互协议,在UDP交互中,存在客户端和服务端,客户端(Client)主要来发送报文,服务端(Server)接收来自客户端的报文,在进行报文交互的时候,UDP是非连接的,客户端发送一个报文到指定端口,然后服务端监听了这个端口,如果收到这个报文,然后就进行处理,如果收不到,或者因为处理报文的消息队列太大,被抛弃了,就导致服务端无法收到这个报文。给大家举个例子,敌人扔了一个手雷过来,队长喊了一声“卧倒!”,有的士兵耳朵正好听到,就立马卧倒了,有的士兵可能正在带着耳机听音乐,没听到这个“报文”,结果就。。。了,所以,UDP的广播是一种不可靠的可以一对多的网络交互。
请添加图片描述

客户端实现方法

客户端思路

1、主要利用C++的 socket 创建一个udp socket.
2、利用setsockopt() 对socket进行设置参数选项
3、创建一个struct sockaddr_in 类型的对象,并进行初始化协议类型(ipv4或ipv6)、接收地址(广播地址)和接收端口号。
4、利用 sendto() 函数将udp报文进行发送。

实现代码

client头文件

#ifndef UDPCLIENT_H
#define UDPCLIENT_H

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <string>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>

using namespace std;
class UdpClient
{
public:
    UdpClient();
    void Init(const int port);
    void StartBroadCast(const string info);
    static void* UdpBroadCast(void * arg);

private:
    struct sockaddr_in m_clientAddr;
    string             m_sendMsg;
    pthread_t          m_tids;
};

#endif // UDPCLIENT_H

client cpp文件

#include "udpclient.h"
#include <QDebug>
#include <qjsondocument.h>
#include <QJsonParseError>
#include <QFile>
#include <QJsonObject>
#include <QJsonArray>
#include <QStringLiteral>

UdpClient::UdpClient()
{
    bzero(&m_clientAddr, sizeof(m_clientAddr));
    m_tids = 0;
}

void UdpClient::Init(const int port)
{
    m_clientAddr.sin_family = AF_INET;
    m_clientAddr.sin_addr.s_addr = INADDR_BROADCAST; //广播 IP
    m_clientAddr.sin_port = htons(port); //指定接收端口
}

void UdpClient::StartBroadCast(string info)
{
    m_sendMsg = info;
    if (m_tids == 0) {
        pthread_create(&m_tids, NULL, UdpBroadCast, (void *)this); // 启动线程来发送报文,将类指针传递到线程内
    }
}

void* UdpClient::UdpBroadCast(void * arg)
{
    int           udpSocket = socket(AF_INET, SOCK_DGRAM, 0);// 创建socket对象
    UdpClient     *udp = (UdpClient *)arg;                   //  接收线程传递的类对象指针
    unsigned long addr = INADDR_BROADCAST;                   //  广播地址

    while (1) {
    	// 设置socket的参数
        int ret = setsockopt(udpSocket, SOL_SOCKET, SO_REUSEADDR | SO_BROADCAST, &addr, sizeof(addr));
        if (ret != 0) {
            qDebug()<<"[Client] set udp client options failed";
            close(udpSocket);
            return NULL;
        }

        string sendMsg =udp->m_sendMsg;
        size_t sendLen = sendto(udpSocket, udp->m_sendMsg.c_str(), udp->m_sendMsg.length(),
                     0, (struct sockaddr*)&udp->m_clientAddr, sizeof(udp->m_clientAddr));

		// 发送成功后,会返回发送报文的长度,如果返回长度和报文长度一致,则认为发送成功
        if (sendLen != udp->m_sendMsg.length()) {
            qDebug()<<QString("[CLIENT]send msg lenth error: src len: %1 send len: %2")
                      .arg(sendMsg.length()).arg((int)sendLen);
        } else {
            qDebug()<<"[CLIENT]send success:"<<QString::fromStdString(sendMsg);
        }

        sleep(5);  // 等待5s后发送
    }

    close(udpSocket);
    return NULL;
}

服务端实现方法

服务端思路

1、创建struct sockaddr_in 变量,设置协议族,接收地址,接收端口。
2、创建socket 变量,设置协议族
3、bind() 函数绑定socket
4、recvfrom() 来获取当前的udp报文

实现代码

server 头文件

#ifndef UDPSERVER_H
#define UDPSERVER_H

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <strings.h>
#include <string>
#include <unistd.h>
#include <iostream>
#include <pthread.h>
#include <functional>
using namespace std;

typedef std::function<void(string)> Fun;
typedef void(*CallFun)(string);

class UdpServer
{
public:
    UdpServer();
    int  Init(const int port, const Fun callbackFun = NULL);  //callbackFun 为收到报文的处理函数
    void setCallBack(Fun func);                               //设置回调函数
    int  startListen();                                       //开始监听接口
    static void* recv(void *args);                            // 接收消息函数

private:
    struct sockaddr_in m_srvAddr;
    int     m_udpSocket;
    Fun     m_msgProcFun;
};

#endif // UDPSERVER_H

server cpp文件

#include "udpserver.h"
#include <string.h>
#include <qdebug.h>
UdpServer::UdpServer()
{
    bzero(&m_srvAddr, sizeof(m_srvAddr));
    m_msgProcFun = NULL;
}

int UdpServer::Init(const int port, const Fun callbackFun)
{
    m_srvAddr.sin_family = AF_INET;
    m_srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    m_srvAddr.sin_port = htons(port);
    m_msgProcFun = callbackFun;
    return startListen();
}

int UdpServer::startListen()
{
    m_udpSocket = socket(AF_INET, SOCK_DGRAM, 0);

    int ret = bind(m_udpSocket,(sockaddr*)&m_srvAddr,sizeof(sockaddr));
    if (ret != 0) {
        return ret;
    }

    pthread_t tids;
    ret = pthread_create(&tids, NULL, recv, (void *)this);        // 采用线程来处理udp报文
    if (ret != 0) {
        qDebug()<<"[SERVER]create thread failed";
        return ret;
    }

    return 0;
}

void* UdpServer::recv(void *args)
{
	sockaddr_in srv_Addr;
    UdpServer*  udp = (UdpServer*)args;
    socklen_t   addr_len = sizeof(srv_Addr);
    char        *buff = new char[100];
    
    while(1) {
        memset(buff, 0, 100);
        size_t sz = recvfrom(udp->m_udpSocket, buff, 100, 0,(struct sockaddr*)&srv_Addr,&addr_len);
        qDebug()<<buff;

        string data = buff;
        data = data.substr(16);  // 取前16个字节的数据
        if (udp->m_msgProcFun) {
            udp->m_msgProcFun(data);
        }
    }

    return NULL;
}

void UdpServer::setCallBack(Fun func)
{
    m_msgProcFun = func;
}

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值