C语言网络编程实现组播(多播)

news2024/10/7 4:30:20

1、组播IP划分

224.0.0.0~224.0.0.255		为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
224.0.1.0~224.0.1.255		是公用组播地址,可以用于Internet;欲使用需申请。
224.0.2.0~238.255.255.255	为用户可用的组播地址(临时组地址),全网范围内有效;
239.0.0.0~239.255.255.255	为本地管理组播地址,仅在特定的本地范围内有效。

2、多播地址与 MAC 地址的映射

IPv4 的 D 类地址是多播地址。IEEE 把一块以太网多播组地址分给 IANA 以支持IP多播。块的地址都以01:00:5e 开头,第 25 位为 0,低 23 位为 IPv4 多播地址( D类地址 )的低 23 位。
IPv4 多播地址与 MAC 地址的映射关系如图所示



由于多播地址( D类地址 )中的最高 5bit 在映射过程中被忽略,因此每个以太网多播地址对应的多播组是不唯一的。32 个不同的多播组号被映射为一个以太网地址。例如,多播地址 224.128.64.32(十六进制 e0.80.40.20)和 224.0.64.32(十六进制 e0.00.40.20)都映射为同一以太网地址01:00:5e:00:40:20。

既然地址映射是不唯一的,那么设备驱动程序或IP层就必须对数据报进行过滤。因为网卡可能接收到主机不想接收的多播数据帧,如下图,假如主机 1 加入的多播为 224.128.64.32,主机 2 加入的多播为 224.0.64.32,我们想给 224.0.64.32 所在的多播组 ( 主机 2 ) 发送信息,数据经过网卡时,224.128.64.32 (主机 1 ) 和 224.0.64.32 (主机 2 ) 所在多播组的网卡都会收到数据,因为它们的 MAC 地址都是 01:00:5e:00:40:20。这时候,如果网卡不提供足够的多播数据帧过滤功能,设备驱动程序就必须接收所有多播数据帧,然后对它们进行过滤,这个过滤过程是网络驱动或IP层自动完成。

4、套接字选项

#include <sys/types.h>
#include <sys/socket.h>

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t*optlen);

sockfd:标识一个套接口的描述字。
level:选项定义的层次;支持
SOL_SOCKET
IPPROTO_TCP
IPPROTO_IP
IPPROTO_IPV6

optname:需设置的选项。
optval:指针,指向存放选项待设置的新值的缓冲区。
optlen:optval缓冲区长度。 

a、选项 IP_MULTICASE_TTL

允许设置超时TTL,范围为0~255之间的任何值,例如:

unsigned char ttl=255; 
setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));

 b、选项IP_MULTICAST_IF

用于设置组播的默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据。例如:

struct in_addr
{
    in_addr_t s_addr;
}
struct in_addr addr; 
setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr))

c 、选项IP_MULTICAST_LOOP

用于控制数据是否回送到本地的回环接口。例如:

unsigned char loop; 
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop))

参数loop设置为0禁止回送,设置为1允许回送

d、选项IP_ADD_MEMBERSHIP

加入某个广播组,之后就可以向这个广播组发送数据或者从广播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的广播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。例如:

struct ip_mreq 
{ 
      struct in_addr imn_multiaddr; /*加入或者退出的广播组IP地址*/ 
      struct in_addr imr_interface; /*加入或者退出的网络接口IP地址*/ 
}

struct ip_mreq mreq; 
setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))

 e、选项IP_DROP_MEMBERSHIP

用于从一个广播组中退出。例如:

struct ip_mreq mreq; 
setsockopt(s,IPPROTP_IP,IP_DROP_MEMBERSHIP,&mreq,sizeof(sreq))

其中mreq包含了在IP_ADD_MEMBERSHIP中相同的值.
 

f、选项SO_REUSEADDR

是一个 socket 选项,用于在关闭 TCP 连接后立即再次使用相同的地址和端口。如果不使用该选项,那么一般情况下要等待一段时间(TIME_WAIT)才能再次使用相同的地址和端口。

int opt = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void  *)&opt, sizeof(opt));

g、选项SO_BINDTODEVICE

指定网卡去发送数据
 

struct ifreq
{
    char ifr_name[IFNAMSIZ]; /* Interface name */
    union {

        struct sockaddrifr_addr;
        struct sockaddrifr_dstaddr;
        struct sockaddrifr_broadaddr;
        struct sockaddrifr_netmask;
        struct sockaddrifr_hwaddr;
        short ifr_flags;
        int ifr_ifindex;
        int ifr_metric;
        int ifr_mtu;
        struct ifmapifr_map;
        char ifr_slave[IFNAMSIZ];
        char ifr_newname[IFNAMSIZ];
        char *ifr_data;
    };
};

struct ifreq interface;
memset(&interface,0,sizeof(interface));
strncpy(interface.ifr_ifrn.ifrn_name, “br0”, strlen(ifname));
setsockopt(fd , SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface))

5、文件控制

#include <fcntl.h>

int fcntl(int fd, int cmd);

int curFlags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, curFlags|O_NONBLOCK);设置非阻塞模式

6、接收示例

/* 
*broadcast_client.c - 多播的客户端 
*/ 
#define MCAST_PORT 8888; 
#define MCAST_ADDR "224.0.0.88" /*一个局部连接多播地址,路由器不进行转发*/ 
#define MCAST_INTERVAL 5 /*发送间隔时间*/ 
#define BUFF_SIZE 256 /*接收缓冲区大小*/
 
int main(int argc, char*argv[]) 
{ 
	int s; /*套接字文件描述符*/ 
	struct sockaddr_in local_addr; /*本地地址*/ 
	int err = -1; 

	s = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/ 
	if (s == -1) { 
		perror("socket()"); 
		return -1; 
	} 
      
										/*初始化地址*/ 
	memset(&local_addr, 0, sizeof(local_addr)); 
	local_addr.sin_family = AF_INET; 
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
	local_addr.sin_port = htons(MCAST_PORT); 

								/*绑定socket*/ 
	err = bind(s,(struct sockaddr*)&local_addr, sizeof(local_addr)) ; 
	if(err < 0) { 
		perror("bind()"); 
		return -2; 
	} 

										/*设置回环许可*/ 
	int loop = 1; 
	err = setsockopt(s,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop)); 
	if(err < 0) { 
		perror("setsockopt():IP_MULTICAST_LOOP"); 
		return -3; 
	} 

	struct ip_mreq mreq; /*加入广播组*/ 
	mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*广播地址*/ 
	mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/ 
											/*将本机加入广播组*/ 
	err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof 
	(mreq)); 
	if (err < 0) { 
		perror("setsockopt():IP_ADD_MEMBERSHIP"); 
		return -4; 
	} 
      
	int times = 0; 
	int addr_len = 0; 
	char buff[BUFF_SIZE]; 
	int n = 0; 
						/*循环接收广播组的消息,5次后退出*/ 
	for(times = 0;times<5;times++)  { 
		
		addr_len = sizeof(local_addr); 
		memset(buff, 0, BUFF_SIZE); /*清空接收缓冲区*/ 
										/*接收数据*/ 
		n = recvfrom(s, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr, &addr_len); 
		if( n== -1)  { 
			perror("recvfrom()"); 
		} 
										/*打印信息*/ 
		printf("Recv %dst message from server:%s\n", times, buff); 
		sleep(MCAST_INTERVAL); 
	} 

									/*退出广播组*/ 
	err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof 
	(mreq)); 

	close(s); 
	return 0; 
}

7、发送示例

/* 
*broadcast_server.c - 多播服务程序 
*/ 
#define MCAST_PORT 8888; 
#define MCAST_ADDR "224.0.0.88"/ /*一个局部连接多播地址,路由器不进行转发*/ 
#define MCAST_DATA "BROADCAST TEST DATA" /*多播发送的数据* 
#define MCAST_INTERVAL 5 /*发送间隔时间*/ 
int main(int argc, char*argv) 
{ 
    int s; 
    
    struct sockaddr_in mcast_addr; 
    s = socket(AF_INET, SOCK_DGRAM, 0); /*建立套接字*/ 
    
    if (s == -1) { 
        perror("socket()"); 
        return -1; 
    } 

    memset(&mcast_addr, 0, sizeof(mcast_addr));    /*初始化IP多播地址为0*/ 
    
    mcast_addr.sin_family = AF_INET;             /*设置协议族类行为AF*/ 
    mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);/*设置多播IP地址*/ 
    mcast_addr.sin_port = htons(MCAST_PORT);             /*设置多播端口*/ 

    /*向多播地址发送数据*/ 
    while(1) { 
    
        int n = sendto(s,MCAST_DATA, sizeof(MCAST_DATA),0, (struct sockaddr*)&mcast_addr, sizeof(mcast_addr)) ; 
        
        if( n < 0) { 
            perror("sendto()"); 
            return -2; 
        } 

        sleep(MCAST_INTERVAL); /*等待一段时间*/ 
    } 

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/952062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

春秋云镜 CVE-2018-2894

春秋云镜 CVE-2018-2894 Weblogic 任意文件上传漏洞 靶标介绍 Oracle Fusion Middleware 的 Oracle WebLogic Server 组件中的漏洞&#xff08;子组件&#xff1a;WLS - Web Services&#xff09;。受影响的受支持版本包括 12.1.3.0、12.2.1.2 和 12.2.1.3。易于利用的漏洞允…

怎样做一个知识库网站

经济和信息技术的蓬勃发展&#xff0c;知识资源成为了企业非常重要的无形资产。 当前&#xff0c;企业的核心竞争力不仅取决于硬件设备、财务实力、资源多寡、人员数量等生产因素&#xff0c;更加取决于企业对于知识的掌握、运用、传承和创新。 制作企业知识库&#xff0c;传…

静态路由(详细理解+实例精讲)

系列文章目录 华为数通学习&#xff08;6&#xff09; 前言 一&#xff0c;静态路由 二&#xff0c;静态路由配置 三&#xff0c;缺省路由 四&#xff0c;缺省路由应用场景 总结 前言 随着华为公司的不断发展&#xff0c;数据通信这门技术也越来越重要&#xff0c;很多人…

js数据类型?如何判断js数据类型?

在JavaScript中&#xff0c;有以下几种数据类型&#xff1a; 基本数据类型&#xff08;Primitive Data Types&#xff09;&#xff1a; String&#xff08;字符串&#xff09;&#xff1a;表示文本数据&#xff0c;使用引号&#xff08;单引号或双引号&#xff09;括起来。Numb…

Android自定义view流程

Android自定义view流程,主要目的是总结实现过程中的思路以及一些需要注意的地方。 首先&#xff0c;我们先来看一张效果图&#xff1a; 实现逻辑 重新指定View宽高绘制外圆圆弧背景及进度绘制中圆圆弧背景及进度绘制内圆圆弧背景及进度 知识点 onMeasure 用于测量View的大小…

读SQL学习指南(第3版)笔记08_视图和索引

1. 精心设计的应用程序通常会在保持实现细节私有的同时公开公有接口&#xff0c;以便未来在不影响最终用户的情况下修改设计 2. 视图 2.1. 不同于数据表&#xff0c;视图并不涉及数据存储&#xff0c;不用担心视图会填满你的磁盘空间 2.2. 一种数据查询机制 2.3. 从用户的视…

基于swing的中国象棋java小游戏jsp源代码Mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、主要功能 可以实现双人下棋&#xff0c;可以悔棋&#xff0c;可…

R语言其他相关函数(各函数解析含实例,可供查询)

目录 一.函数相关 1.函数定义&#xff1a;function 2.调用文件:source 3. Call 4.Recall 5.browser 6.debug和undebug 7.trace 8.traceback 9.options 10.missing 11.nargs 12.stop 13.指定退出时执行的表达式 14.expression和eval 15.system.time 16. invisi…

Springboot+mybatis-plus+dynamic-datasource 切换数据源失败问题总结

Springbootmybatis-plusdynamic-datasourceDruid 多数据源 切换数据源失败总结 文章目录 Springbootmybatis-plusdynamic-datasourceDruid 多数据源 切换数据源失败总结0.前言1. dynamic-datasource 切换数据源失败场景总结1. spring-batch整合情况下切换数据源异常解决办法&am…

Matlab图像处理-图像缩放

基本概念 图像缩放是指将给定的图像在x轴方向按比例缩放a倍&#xff0c;在y轴方向按比例缩放b倍&#xff0c;从而获得一幅新的图像。 如果ab&#xff0c;即在x轴方向和y轴方向缩放的比率相同&#xff0c;则称这样的比例缩放为图像的全比例缩放。 如果a≠b&#xff0c;图像比…

Spring: HiddenHttpMethodFilter的用法及原理

作用&#xff1a;将html表单提交的post请求转换为put请求或delete请求发给接口。 html不支持put和delete. 一、web.xml中配置过滤器 <filter><filter-name>HiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenH…

认识SQL sever

目录 一、数据库的概念 1.1数据库的基本概念 1.2对数据库的了解 二、数据库的分类 2.1关系型数据库&#xff08;RDBMS&#xff09;&#xff1a; 2.2非关系型数据库&#xff08;NoSQL&#xff09;&#xff1a; 2.3混合数据库&#xff1a; 2.4数据仓库&#xff1a; 2.5嵌…

二叉树中的堆

堆的概念和结构 大堆&#xff1a; 树中的任何一个父亲都大于等于孩子 小堆&#xff1a; 树中的任何一个父亲都小于等于孩子 堆在逻辑上是二叉树来存储的&#xff0c;就是在我们的想象中他是按二叉树来存储的&#xff0c;但是在实际上&#xff0c;它是以数组的形式来存储的&…

软考A计划-网络工程师-复习背熟-路由器与交换配置和网络安全

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

移动端如何适配不同的屏幕尺寸

在移动端开发中&#xff0c;适配不同的屏幕尺寸是一个重要的考虑因素。以下是一些常用的方法来实现移动端的屏幕适配&#xff1a; 使用响应式布局&#xff1a;使用CSS媒体查询和弹性布局来根据屏幕尺寸调整页面布局和元素大小。通过设置百分比、em或rem单位来实现元素的相对大小…

ICCV 2023 | 港中文MMLab: 多帧光流估计模型VideoFlow,首次实现亚像素级别误差

本文提出了一个多帧光流估计模型 VideoFlow&#xff0c;旨在充分挖掘视频中的时序信息和运动规律&#xff0c;避免当前主流方法只以两帧图片作为输入而面临的信息瓶颈&#xff0c;显著提升了光流估计的性能。 在公开的 Sintel Bechmark 上&#xff0c;VideoFlow 在 Clean 和 Fi…

过期订单关闭

由于Redis具有过期监听的功能&#xff0c;于是就有人拿它来实现过期订单关闭&#xff0c;但是这个方案并不完美。今天来聊聊11种实现订单定时关闭的方案&#xff0c;总有一种适合你&#xff01; 在电商、支付等系统中&#xff0c;一般都是先创建订单&#xff08;支付单&#x…

七牛云OSS存储

前言: 七牛云的存储项目的附件,需要开发一套七牛云的工具类,可以使用该工具类进行七牛云服务器进行文件的上传与下载操作; 七牛云的文档学习: 相关的依赖项的配置: <dependency><groupId>com.amazonaws</groupId><artifactId>aws-java-sdk-s3…

如何改善客户服务?

提供优质的客户服务是经营成功企业的一个重要方面。满意的客户更有可能成为回头客&#xff0c;并向他人推荐你的产品或服务。在当今竞争激烈的市场中&#xff0c;良好的客户服务可能是一家繁荣的企业与一家艰难生存的企业的区别。今天&#xff0c;我们就改善客户服务6个行之有效…

面试官眼中的理想候选人:如何成为他们的首选

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…