当前位置:   article > 正文

C/C++ 实现UDP发送或接收组播消息,并可指定接收发送网卡_c++ udp发送和接收

c++ udp发送和接收

一、发送端代码

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include "UDPOperation.h"
#include "GlobalVariable.h"
#include "Logger.h"
#include "EndException.h"
#include "BaseException.h"

UDPOperation::UDPOperation(char* remote_host, int remote_port, char* interface) : fd(-1)
{
    // 创建通信的套接字
    this->remote_host = remote_host;
    this->remote_port = remote_port;
    this->interface = interface;

    memset(&(this->cliaddr), 0, sizeof(sockaddr_in));
    this->cliaddr.sin_family = AF_INET;
    this->cliaddr.sin_port = htons(this->remote_port); // 接收端需要绑定remote_port端口
}

UDPOperation::~UDPOperation() {}

bool UDPOperation::create_udpsocket()
{
    this->fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (this->fd == -1)
    {
        LOG_ERROR("Socket creation failed: %s", strerror(errno));
        throw EndException(errno, strerror(errno));
    }

    inet_pton(AF_INET, this->remote_host, &this->cliaddr.sin_addr.s_addr);

    hostent* host = gethostbyname(remote_host);
    unsigned long hostip = *(unsigned long *)host->h_addr;
    
    this->cliaddr.sin_addr.s_addr = hostip;
    
    unsigned char net = hostip & 0xff;
    if (net > 223 && net < 240)  // 如果是多播
    {   
        char numeric_ip[32] = "\0";
        get_ifaddr (numeric_ip);
        struct in_addr outputif;
        outputif.s_addr = inet_addr (numeric_ip);
        LOG_INFO("interface = %s, numeric_ip = %s", interface, numeric_ip);
        if (setsockopt(this->fd, IPPROTO_IP, IP_MULTICAST_IF, (char* ) &outputif, sizeof(struct in_addr)))
        {
            throw EndException(errno, strerror(errno));
        }
    }

    return true;
}

int UDPOperation::get_ifaddr(char* addr)
{
  int sock = socket (AF_INET, SOCK_DGRAM, 0);

  struct ifreq ifr;
  memset (&ifr, 0, sizeof (ifr));
  strcpy (ifr.ifr_name, interface);
  if (ioctl (sock, SIOCGIFADDR, &ifr) < 0) {
    close (sock);
    throw EndException(errno, strerror(errno));
    return 1;
  }
  
  strcpy (addr, inet_ntoa(((struct sockaddr_in* ) &(ifr.ifr_addr))->sin_addr));
  close (sock);

  return 0;
}

void UDPOperation::destory_udpsocket()
{
    close(this->fd);
}

bool UDPOperation::send_buffer(char *buffer)
{
    socklen_t len = sizeof(struct sockaddr_in);
    // 数据广播
    int t = sendto(this->fd, buffer, SEND_UDP_PER_TSPACKET_SIZE, 0, (struct sockaddr *)&cliaddr, len);
    if (t == -1)
    {
        LOG_ERROR("Socket send failed: %s", strerror(errno));
        throw BaseException(errno, strerror(errno));
    }
    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

二、接收端代码

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include "UDPOperation.h"
#include "GlobalVariable.h"
#include "Logger.h"
#include "EndException.h"
#include "BaseException.h"

UDPOperation::UDPOperation(std::string remote_host, int remote_port, char* interface) : fd(-1)
{
    // 创建通信的套接字
    this->remote_host = remote_host;
    this->remote_port = remote_port;
    this->interface = interface;

    memset(&(this->cliaddr), 0, sizeof(sockaddr_in));
    this->cliaddr.sin_family = AF_INET;
    this->cliaddr.sin_addr.s_addr = inet_addr(this->remote_host.c_str());
    this->cliaddr.sin_port = htons(this->remote_port); // 接收端需要绑定remote_port端口
}

UDPOperation::~UDPOperation() {}

bool UDPOperation::create_udpsocket()
{
    this->fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (this->fd == -1)
    {
        LOG_ERROR("Socket creation failed: %s", strerror(errno));
        throw EndException(errno, strerror(errno));
    }

    // 设置socket选项,允许重用地址  
    int reuse = 1;  
    if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {  
        LOG_ERROR("Error setting socket option: %s", strerror(errno));
        throw EndException(errno, strerror(errno)); 
    }  

    struct sockaddr_in local_addr;      //local address
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = inet_addr("0.0.0.0");   // 设定本地监听必须是0.0.0.0 这里是关键!
    local_addr.sin_port = htons(remote_port);             //this port must be the group port
    //建立本地捆绑(主机地址/端口号)
    if (bind(fd, (struct sockaddr*)&local_addr, sizeof(local_addr)) != 0)
    {
        LOG_ERROR("Error binding socket: %s", strerror(errno));
        throw EndException(errno, strerror(errno)); 
    }
    
    // 如果是组播 加入组播
    int net = stoi(remote_host.substr(0, remote_host.find('.')));
    if (net >= 224 && net <= 239)
    {
        struct ip_mreq mreq;
        mreq.imr_multiaddr.s_addr = inet_addr(this->remote_host.c_str());
        if(strlen(interface) == 0){
            mreq.imr_interface.s_addr = htonl(INADDR_ANY);                //任意接口接收组播信息
        }else{
            char numeric_ip[32] = "\0";
            get_ifaddr (numeric_ip);
            LOG_INFO("interface = %s, numeric_ip = %s", interface, numeric_ip);
            mreq.imr_interface.s_addr = inet_addr(numeric_ip);    //指定新接口接收组播信息
        }

        if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0) {
            LOG_ERROR("Error setting socket option for multicast: %s", strerror(errno));
            throw EndException(errno, strerror(errno));
        }
    }
    return true;
}

void UDPOperation::destory_udpsocket()
{
    close(this->fd);
}

int UDPOperation::recv_buffer(uint8_t *buffer, int size)
{
    socklen_t len = sizeof(struct sockaddr_in);
    int bytes_received = recvfrom(this->fd, buffer, size, 0, (struct sockaddr *)&this->cliaddr, &len);  
    if (bytes_received < 0) {  
        LOG_ERROR("Error receiving data: %s", strerror(errno));
        throw BaseException(errno, strerror(errno)); 
    }  
    return bytes_received;
}

int UDPOperation::get_ifaddr(char* addr)
{
    int sock = socket (AF_INET, SOCK_DGRAM, 0);

    struct ifreq ifr;
    memset (&ifr, 0, sizeof (ifr));
    strcpy (ifr.ifr_name, interface);
    if (ioctl (sock, SIOCGIFADDR, &ifr) < 0) {
        close (sock);
        throw EndException(errno, strerror(errno));
        return 1;
    }

    strcpy (addr, inet_ntoa(((struct sockaddr_in* ) &(ifr.ifr_addr))->sin_addr));
    close (sock);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114

三、若udp组播接收不到数据可能是如下原因

# 2. 看系统有没有过滤组播包:
# 2.1 看接受组播的网卡是否过滤了:
cat /proc/sys/net/ipv4/conf/en4/rp_filter
# 如果是0, good。
# 2.2 看all网卡是否过滤了:
cat /proc/sys/net/ipv4/conf/all/rp_filter
# 如果是0, good。
# 这两个值都必须是0,才行!如果不是0,这样修改:
# 2.3 临时修改取消过滤:
sudo sysctl -w net.ipv4.conf.en4.rp_filter=0
sudo sysctl -w net.ipv4.conf.all.rp_filter=0
# 2.4 永久修改取消过滤(重启亦有效):
sudo vi /etc/sysctl.conf
# 改为:
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.rp_filter=0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

rp_filter参数详细介绍:
rp_filter参数有三个值,0、1、2,具体含义:

0:不开启源地址校验。

1:开启严格的反向路径校验。对每个进来的数据包,校验其反向路径是否是最佳路径。如果反向路径不是最佳路径, 则直接丢弃该数据包。

2:开启松散的反向路径校验。对每个进来的数据包,校验其源地址是否可达,即反向路径是否能通(通过任意网口),如果反向路径不同,则直接丢弃该数据包。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/471458
推荐阅读
相关标签
  

闽ICP备14008679号