当前位置: 首页 > news >正文

C/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;
}

二、接收端代码

#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 addressmemset(&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;
}

三、若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

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

0:不开启源地址校验。

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

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

http://www.lryc.cn/news/213857.html

相关文章:

  • 纬创出售印度子公司给塔塔集团,结束iPhone代工业务 | 百能云芯
  • vue手机项目如何控制手电筒打开与关闭
  • 电商课堂|5分钟了解电商数据分析完整流程,建议收藏!
  • Redis测试新手入门教程
  • Linux内核是如何创建进程?
  • IDEA 使用技巧
  • 安防监控项目---web网页通过A9控制Zigbee终端节点的风扇
  • Ubuntu 22.04 在登录界面循环
  • 【C++ 系列文章 -- 程序员考试 201805 下午场 C++ 专题 】
  • Python如何使用datetime模块进行日期和时间的操作
  • flutter之bloc使用详解
  • 记一次 .NET 某工厂无人车调度系统 线程爆高分析
  • 高等数学啃书汇总重难点(九)多元函数微分法及其应用
  • Vue3前端100个必要的知识点
  • CCS3列表和超链接样式
  • vue手机项目如何控制蓝牙连接
  • 遥遥领先,免费开源的django4-vue3项目
  • 视频平台跨网级联视频压缩解决方案
  • 利用python进行数据分析 pdf
  • Day46.算法训练
  • 基于YOLOv8模型暗夜下人脸目标检测系统(PyTorch+Pyside6+YOLOv8模型)
  • 如何在 Photoshop 中使用位图模式制作自定义音乐海报
  • 1 — NLP 的文本预处理技术
  • TypeScript之泛型
  • 一个小妙招从Prompt菜鸟秒变专家!加州大学提出PromptAgent,帮你高效使用ChatGPT!
  • Netty通信框架
  • 6西格玛质量标准: 提升业务效率的关键
  • OpenGL ES相关库加载3D 车辆模型
  • 云原生环境下JAVA应用容器JVM内存如何配置?—— 筑梦之路
  • 防雷接地测试方法完整方案