TCP/IP详解 第九章 IP组播基础及工作原理

转载请声明博主:TCP/IP详解 第九章 IP组播基础及工作原理_cft56200_ln的博客-CSDN博客_ipv4组播原理

一、寻址模式

①寻址模式—概述

计算机网络中,寻址模式是指我们如何寻址网络上的主机的机制。 

IPv4IPv6提供几种类型的模式,通过这些模式可以寻址单个主机,可

一次寻址多于一个主机,或者可以寻址最近距离的主机

有些书籍也将寻址模式称作IP网络的数据传输方式

②寻址模式单播

在单播寻址模式下,IP地址段中唯一标识。 IP数据包包含源IP地址和目标IP地址。 主机接口配备有在该网段中唯一的IP地址。 网络交换机或路由器在接收到指定到单个主机的单播IP分组时,发送到其连接到该特定主机的其输出接口之一

TCP/IP详解 第九章 IP组播基础及工作原理_第1张图片

③寻址模式广播

广播的定义:

       主机之间“一对所有”的通信模式,网络对其中每一台主机发送信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要),由于其不用路径选择,所以其网络成本很低廉。有线电视网就是典型的广播型网络,我们的电视机实际上是接收到所有频道的信号,但只将一个频道信号还原成画面。在数据网络中也允许广播的存在,但其被限制在二层交换机的局域网范围内,禁止广播数据穿过路由器,防止广播数据影响大面积的主机。

TCP/IP详解 第九章 IP组播基础及工作原理_第2张图片

广播的缺点:

1、无法针对每个客户的要求和时间及时提供个性化服务。

2、广播禁止在Internet宽带网上传输。

3、网络允许提供数据的宽带有限,客户端的最大宽带等于服务总宽带。如

      有线电视的客户端的线路支持100个频道(如果采用数字压缩技术,理论

      上可以提供500个频道),即使服务商有更大的财力配置更多的发送设备、

      改成光纤主干,也无法超越此极限。也就是说无法向众多客户提供更多

      样化、更加个性化的服务

4IPv6已经禁止使用广播

广播的优点:

网络设备简单,维护简单,布网成本低廉。

广播的应用:

1ARP协议通过链路层广播定位具有IP地址的主机的MAC地址

2、主机通过广播向DHCP服务器申请IP地址

④寻址模式组播

IPv6组播模式与IPv4相同。 目的地为多个主机的数据包在特殊的多播地址上发送。 所有对该组播信息感兴趣的主机需要首先加入该组播组。 所有加入组的接口接收组播报文并处理,其他对组播报文不感兴趣的主机忽略组播信息。

TCP/IP详解 第九章 IP组播基础及工作原理_第3张图片

组播的优点:

 1 、发送方仅发一份数据包,此后数据包只是在需要复制分发的地方才会被复制分发,每一网段中都将保持只有一份数据流,减轻服务器的负担,节省网络带宽。

 2 、由于组播协议是根据接受者的需要对数据流进行接收,所以服务端的服务总带宽不受客户接入端带宽的限制。 IP 协议允许有 2 亿 6 千多万个组播,所以其提供的服务可以非常丰富。

 3 、此协议和单播协议一样允许在 Internet 宽带网上传输。

组播的缺点:

组播是基于UDP的。

尽力投递:报文丢失是不可避免的。因此组播应用程序不能依赖组播网络进行可靠性保证,必须针对组播网络的这个特点进行特别设计。“可靠组播”目前仍然处于研究阶段。

没有拥塞避免机制:缺少TCP窗口机制和慢启动机制,组播可能会出现拥塞。如果可能的话,组播应用程序应该尝试检测并避免拥塞。

报文重复:某些组播协议的特殊机制(如Assert机制和SPT切换机制)可能会造成偶尔的数据包的重复。组播应用程序应该容忍这种现象。

报文失序:同样组播协议有的时候会造成报文到达的次序错乱,组播应用程序必须自己采用某种手段进行纠正(比如缓冲池机制等)。

组播应用

组播应用大致可以分为三类: 点对多点应用,多点对多点应用和多点对点应用。

组播应用--对多点

点对多点应用是指一个发送者,多个接收者的应用形式,这是最常见的组播应用形式

媒体广播:如演讲、演示、会议等按日程进行的事件。其传统媒体分发手段通常采用电视和广播

媒体推送:如新闻标题、天气变化、运动比分等一些非商业关键性的动态变化的信息

信息缓存: 如网站信息、执行代码和其他基于文件的分布式复制或缓存更新

事件通知:如网络时间、组播会话日程、随机数字、密钥、配置更新、有效范围的网络警报或其他有用信息

状态监视:如股票价格、传感设备、安全系统、生产信息或其他实时信息多点对多

组播应用--点对多

多点对多点应用是指多个发送者和多个接收者的应用形式。通常,每个接收者可以接收多个发送者发送的数据,同时,每个发送者可以把数据发送给多个接收者。

典型应用包括:

多点会议:通常音/视频和白板应用构成多点会议应用

协同处理:如共享文档的编辑

远程学习:这实际上是媒体广播应用加上对上行数据流(允许学生向老师提问)的支持

讨论组:类似于基于文本的多点会议,还可以提供一些模拟的表达。

分布式交互模拟(DIS):它对带宽和时延的要求较高。

多人游戏:多人游戏是一种带讨论组能力的简单分布式交互模拟。它对带宽和时延的要求都比较高。

Jam Session:这是一种音频编码共享应用。它对带宽和时延的要求都比较高。

组播应用--多点对点

多点对点应用是指多个发送者,一个接收者的应用形式。通常是双向请求响应应用,任何一端(多点或点)都有可能发起请求。典型应用包括:

资源查找:如服务定位,它要求的带宽较低,对时延的要求一般。

数据收集:它是点对多点应用中状态监视应用的反向过程。它可能由多个传感设备把数据发回给一个数据收集主机

网络竟拍:拍卖者拍卖产品,而多个竟拍者把标价发回给拍卖者。

信息询问: 询问者发送一个询问,所有被询问者返回应答

Juke Box:如支持准点播(Near-On-Demand)的音视频倒放。通常接收者采用“带外的”协议机制(如HTTPRTSPSMTP,也可以采用组播方式)发送倒放请求给一个调度队列。它对带宽的要求较高,对延时的要求一般。

⑤寻址模式任播

IPv6引入了一种新型的寻址,称为Anycast寻址。 在此寻址模式下,多个接口(主机)被分配相同的任播IP地址。 当主机希望与配备有Anycast IP地址的主机通信时,发送单播消息。 在复杂的路由机制的帮助下,在路由成本方面,该单播消息被递送到最接近发送方的主机。

TCP/IP详解 第九章 IP组播基础及工作原理_第4张图片

我们以一个位于所有大陆的TutorialPoints.com Web服务器为例。 假设所有Web服务器都分配有单个IPv6 Anycast IP地址。 现在,当欧洲的用户希望访问TutorialsPoint.com时,DNS指向物理上位于欧洲的服务器。 如果来自印度的用户尝试访问Tutorialspoint.com,则DNS将指向仅位于亚洲的Web服务器。最接近或最接近的术语用于路由成本。

上图中,当客户端计算机尝试访问服务器时,请求转发到具有最低路由开销的服务器。

TCP/IP详解 第九章 IP组播基础及工作原理_第5张图片

、组播详解

①组播方式数据传输示意图

TCP/IP详解 第九章 IP组播基础及工作原理_第6张图片

②组播基本概念

组播传输的特点是一点发出,多点接收。组播方式数据传输示意图网络中存在信息发送源Source,感兴趣的用户HostAHostC提出信息需求,Source发出的数据只有HostAHostC会接收到

在组播通信中,需要理解以下几个重要的基本概念

1、组播组:用组播IP地址进行标识的一个集合,是一个组播成员的集合,各组播成员共享这一个组播组IP地址

2、组播源:以组播组IP地址为目的地址(组播源配置的也是单播IP地址),发送IP报文的信源称为组播源。但组播源通常不需要加入组播组,否则自己接收自己发送出去的数据了。上图中的Source就是一个组播源。一个组播源可以同时向多个组播组发送数据,多个组播源也可以同时向一个组播组发送报文

3、组播组成员:所有加入某组播组的主机便成为该组播组的成员。如上图中的HostAHostC。组播组中的成员是动态的,主机可以在任何时刻加入或离开组播组。组播组成员可以广泛地分布在网络中的任何地方

4、组播路由器:支持三层组播功能的路由器或三层交换机(它们不是组播组成员),如上图中的各个Router。组播路由器不仅能够提供组播路由功能,也能够在与用户连接的末梢网段上提供组播组成员的管理功能

③典型IP组播模型

根据对组播源处理方式的不同,IP组播模型有下列3

ASMAny-SourceMulticast,任意源组播

SFMSource-Filtered Multicast,过滤源组播

SSMSource-SpecificMulticast,指定源组播

1ASM模型

      简单说,ASM(任意源组播)模型就是任意源都可以成为组播源。由此可知,ASM模型中的组播源是不限定的,任意一个发送者都可以成为组播源,然后向某组播组地址发送数据,显然安全性较差。接收者通过加入对应的组播组就可以获得发往改组播组的任意组播数据,而且接收者无法预先知道组播源的位置,但可以在任意时间加入或离开该组播组。为提高安全性,可以在路由器上配置针对组播源的过滤策略,允许或禁止来自某些组播源的报文通过。最终从接收者角度看,数据是经过筛选的

      ASM模型中组播组可以使用的组播IP地址为224.0.1.0~231.255.255.255233.0.0.0~238.255.255.255。但要求组播组地址必须整个组播网络中唯一。“唯一”指的是同一时刻一个ASM组播组地址只能被一种组播应用使用。如果有两种不同的应用程序使用了同一个ASM组播组地址发送数据,它们的接收者会同时收到来自两个源的数据。这样一方面会导致网络流量拥塞,另一方面也会给接收者主机造成困扰

2SFM模型

      SFM(过滤源组播)模型继承了ASM模型,从发送者角度来看两者的组播组成员关系完全相同,也可以是任意组播源。但是,在SFM模型中组播上层应用软件可以根据收到的组播包的源IP地址进行过滤,允许或禁止来自某些组播源的包通过。这样一来,接收者就可以只接收允许通过的组播源发送来的组播数据。即SFMASM的基础上添加了组播源过滤策略。

3SSM模型

      现实生活中,用户可能只对某些组播源发送的组播数据感兴趣,而不愿接收其他源发送的数据。SSM(指定源组播)模型就是一种为用户提供能够在客户端指定组播源的传输服务。SSM模型与ASM模型的根本区别在于:SSM模型中的接收者已经通过其他手段预先知道了所需接收组播数据的组播源的具体位置,限定了可接收的组播源。然后,SSM模型使用与ASM/SFM模型不同的组播组地址范围(为232.0.0.0~232.255.255.255)直接在接收者和其指定的组播源之间建立专用的组播转发树。

④IP组播地址

由于组播数据的接收者是一个组播组内的多个主机,因此,需要面对数据源该将数据发往何处、目的地址如何选取的问题,就是组播寻址。与单播中的IP寻址或者MAC寻址一样,为了让组播源和组播成员进行通信,需要提供网络层组播地址,即IP组播地址。同时必须存在一种技术将IP组播地址映射为链路层MAC组播地址

注意:不要认为在IP组播中所有组播设备上的IP地址都是使用组播地址。实际上只有组播组IP地址是组播IP地址,而像组播源、接收者主机的IP地址仍是单播IP地址

1、三层组播IP地址

根据IANAInternetAssigned Numbers Authority,因特网编号授权委员会)规定,IP地址分为五类:即A类、B类、C类、D类和E

播包按照网络规模大小分别使用ABC三类IP地址

组播包的目的地址使用DIP地址,D类地址不能出现在IP包的源IP地址字段(也就是不能作为组播源地址,换言之,组播源的IP地址仍是单播地址)

E类地址保留以后使用

在单播数据传输过程中,一个数据包传输的路径是从源地址路由到目的地址,利用“逐跳(hop-by-hop)”的原理在IP网络中传输。然而在IP组播环境中,数据包的目的地不是一个,而是一组,形成组地址(可理解为所有接收者的单播地址与一个组播组地址形成了映射关系)。所有的数据接收者都加入一个组内,并且一旦加入之后,流向该组地址的数据立即向接收者传输,组中的所有成员都能接收到数据包,这个组就是“组播组”

组播组具有以下几个特点:

1)组播组中的成员是动态的,主机可以在任何时刻加入或离开组播组

2)组播组可以是永久的也可以是临时的

3)由IANA分配组播组地址的组播组称为永久组播组(又称保留组播组)

对于永久组播组,要注意以下几点

1)永久组播组的IP地址保持不变,但组中的成员构成可以发生变化

2)永久组播组中成员的数量可以是任意的,甚至可以是零

3)那些没有保留下来供永久组播组使用的IP组播地址,可以被临时组播组使用

TCP/IP详解 第九章 IP组播基础及工作原理_第7张图片

TCP/IP详解 第九章 IP组播基础及工作原理_第8张图片

2、二层以太网组播MAC地址

       以太网传输单播IP包的时候,目的MAC地址使用的是接收者的MAC地址。但是在传输组播包时,传输目标不再是一个具体的接收者,而是一个成员不确定的组,所以对应也就需要使用组播MAC地址作为目的地址

     IANA规定,组播MAC地址的高25位固定为0000 00010000 0000 0101 1110 0,形成MAC地址25位前缀,MAC地址的低23位为组播IPv4地址的低23位。它们之间的映射关系如下图(组播IPv4地址中的低23位映射到组播MAC地址的低23位)

TCP/IP详解 第九章 IP组播基础及工作原理_第9张图片

由于IPv4组播地址的高4位是1110,代表组播标识,而低28位中只有23位被映射到MAC地址,这样IP地址中就会有5位数据丢失,直接的结果是出现了3225次方)个IP组播地址映射到同一组播MAC地址上。

 IPv6组播MAC地址的高16位为0x3333,低32位是从IPv6组播地址的低32位映射过来的。如下图IPv6组播地址FF1E::F30E101MAC地址映射:

TCP/IP详解 第九章 IP组播基础及工作原理_第10张图片

组播抓包

TCP/IP详解 第九章 IP组播基础及工作原理_第11张图片

实现一套完整的组播服务,需要在网络各个位置部署多种组播协议相互配合,共同运作。不同结构的组播网络所需使用的组播协议不完全一样

        下图是一个典型的单PIM域组播网络示意图,整个组播网络是由路由器或三层交换机+二层交换机组成的

        从图中可以看出在这些组播设备上运行的组播协议包括PIM(协议无关组播,同时由IPv4IPv6版本)、IPv4网络的IGMP(因特网组管理协议),IPv6网络中的MLD(组播监听器发现)、IPv4网络的IGMP Snooping(因特网组管理协议嗅探),IPv6网络中的MLD Snooping(组播监听器发现嗅探)。

⑤IP组播路由表维护协议简介

TCP/IP详解 第九章 IP组播基础及工作原理_第12张图片

实现一套完整的组播服务,需要在网络各个位置部署多种组播协议相互配合,共同运作。不同结构的组播网络所需使用的组播协议不完全一样

        下图是一个典型的单PIM域组播网络示意图,整个组播网络是由路由器或三层交换机+二层交换机组成的

TCP/IP详解 第九章 IP组播基础及工作原理_第13张图片

下图12-7是一个跨PIM-SM域的组播网络示意图,与上图所示的单PIM域组播网络相比,在运行的组播协议上仅需在PIM域边界组播路由器上多了一个实现跨PIM域连接的MSDP(组播源发现协议)。而12-8是一个跨AS域组播网络示意图,与12-7所示的跨PIM域组播网络相比,在运行的组播协议又仅需在AS边界组播路由器上多了一个用于不同AS组播连接的MBGP(组播边界管理协议)

TCP/IP详解 第九章 IP组播基础及工作原理_第14张图片

TCP/IP详解 第九章 IP组播基础及工作原理_第15张图片

1IGMPMLD

IP组播传输模型中,发送者不关心接收者所处的位置,只要将数据发送到约定的目的地址,剩下的工作就交给网络去完成。网络中的路由器设备必须收集接收者的信息,并按照正确的路径实现组播报文的转发和复制

接收者信息的收集和管理的工作通过IGMPInternetGroup Management Protocol,因特网组管理协议)或MLDMulticast Listener Discovery,组播监听器发现)协议来完成的。其中,IGMP用于IPv4网络,MLD用于IPv6网络。用于为主机侧提供组播组成员动态加入与离开服务,为路由器侧提供组成员关系的维护与管理服务,同时与上层组播路由协议进行信息交互

IGMP包含3个版本,分别是IGMPv1IGMPv2IGMPv3。新版本完全兼容旧版本。目前应用最广泛的是IGMPv2。在组播模型方面,3个版本都支持ASM模型;IGMPv3可以直接支持SSM模型,而IGMPv1IGMPv2需要结合SSMMapping技术才能支持SSM模型

IPv6组播中使用MLD协议来替代IGMP协议,也是一种三层组播协议。MLD包含两个版本,分别是MLDv1MLDv2MLDv1的功能与IGMPv2相似;MLDv2的功能与IGMPv3相似。两个MLD版本都支持ASM模型;MLDv2可以直接支持SSM模型,而MLDv1需要结合SSM-Mapping技术才能支持SSM模型

2IGMPSnoopingMLD SnoopingIGMP

SnoopingMLD Snooping协议是运行在组播路由器和用户主机之间的二层交换机上的二层组播协议,配置在VLAN内。其中,IGMP Snooping用于IPv4网络,MLD Snooping用于IPv6网络,用来侦听路由器和主机之间发送的IGMPMLD报文建立组播数据的二层转发表,从而管理和控制组播数据在二层网络中的转发

3PIMMSDP

       组播报文转发路径的建立,有多种组播路由协议可以完成。目前应用广泛的是PIMProtocol Independent Multicast,协议无关组播)协议。PIM是一种域内组播路由协议,当跨PIM域传递组播源信息时,需要MSDPMulticast Source Discovery Protocol,组播源发现协议)支持;当跨AS域建立组播路由时则同时需要MSDPMBGPMultiProtocolBorder Gateway Protocol,组播边界网关协议)支持

       PIM是用于IPv4IPv6组播网络中域内组播路由器之间的组播路由与转发,用来在自治系统AS内发现组播源并构建组播分发树,将信息传递到接收者。在一个小型网络中,所有的组播路由器都在一个PIM组播域内。他可以动态响应网络拓扑变化,维护组播路由表,并按照路由表项执行转发

       MSDP目前仅用于IPv4组播网络中域间组播路由器之间的域间组播源信息共享,但只对ASM服务模型有意义。它可以实现源所在域内的路由器将本地源信息传播给其他域内的路由器,以及不同域的路由器之间传递源信息。为了使不同的PIM-SM域之间组播数据能够互通,需要在域间部署MSDP协议。MSDP通过在各个PIM-SM域之间建立MSDP对等体关系,对等体之间交互SASource Active,源激活)消息来传递组播信息,从而实现接收者主机可以接收其他PIM-SM域的组播源数据

4 RPF检查

TCP/IP详解 第九章 IP组播基础及工作原理_第16张图片

单播路由与转发中,单播报文是沿着一条点到点的路径传输的,路由器只需考虑报文需要到达的位置(即目的地址),就知道应该从哪个接口转发出去。组播路由与转发则不同,因为组播报文的目的地址为组播地址,只是标识了一组接收者,并不是一个可以唯一确定其位置的IP地址,所以无法通过“目的地址”来找到接收者的位置。但是组播报文的来源位置(即源地址)是可以确定的,所以组播报文的转发路径可根据其源地址的逆向查找来确保转发路径的正确性。具体过程如下

1)组播路由器在收到一份组播报文后,会根据报文的源地址通过单播路由表查找到达“报文源”的路由,查看到“报文源”的路由表的出接口是否与收到该组播报文的入接口一致

2)如果一致,则认为该组播报文是从正确的接口到达,从而保证了整个转发路径的正确性和唯一性

3)如果不一致,则认为该组播报文非法,将被直接丢弃

4RPF检查的过程中除依据单播路由表外,还会依据MBGP路由表、组播静态路由表。且缺省情况下,组播静态路由、MBGP路由和单播路由的优先级是依次降低的

、组播socket编程

Linux接收端多播编程步骤:

1. 建立一个 socket
2. 设置多播的参数,例如超时时间 TTL ,本地回环许可 LOOP
3. 加入多播
4. 发送和接收 数据
5. 从多播组 离开

Linux发送端多播编程步骤:

1.建立一个socket

2.指定组播数据输出网口

3.向指定组播地址和端口号发送数据

TCP/IP详解 第九章 IP组播基础及工作原理_第17张图片

TCP/IP详解 第九章 IP组播基础及工作原理_第18张图片

TCP/IP详解 第九章 IP组播基础及工作原理_第19张图片

如果不使用这个函数那么就需要通过路由表来操作数据从哪个网口出去

TCP/IP详解 第九章 IP组播基础及工作原理_第20张图片

TCP/IP详解 第九章 IP组播基础及工作原理_第21张图片

multicast_rcv.c

/*
*broadcast_client.c - 多播的客户端
*/
#include
#include  
#include     
#include      
#include    
#include     
#include    
#include    
 
#define MCAST_PORT 		8888
#define MCAST_ADDR 		"224.0.0.100"     /*一个局部连接多播地址,路由器不进行转发*/
#define MCAST_INTERVAL 	5                        /*发送间隔时间*/
#define BUFF_SIZE 		3000                           /*接收缓冲区大小*/
int main(int argc, char*argv[])
{  
    int s;                                      /*套接字文件描述符*/
    struct sockaddr_in local_addr;              /*本地地址*/
    struct sockaddr_in client_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<30000;times++)
    {
        addr_len = sizeof(client_addr);
        memset(buff, 0, BUFF_SIZE);                 /*清空接收缓冲区*/
        memset(&client_addr,0,sizeof(client_addr));
                                                    /*接收数据*/
        n = recvfrom(s, buff, BUFF_SIZE, 0,(struct sockaddr*)&client_addr,&addr_len);
        if( n== -1)
        {
            perror("recvfrom()");
        }
                                                /*打印信息*/
        printf("Recv %dst message from server:%s #######recvfrom btytes=%d\n", times, buff,n);
        printf("client ip:0x%x port:0x%x\n",local_addr.sin_addr.s_addr,local_addr.sin_port);
        //sleep(MCAST_INTERVAL);
    }
   
                                                    /*退出多播组*/
    err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof
    (mreq));
       
    close(s);
    return 0;
}

multicast_send.c

/*
*broadcast_server.c - 多播服务程序
*/
#include
#include  
#include     
#include      
#include    
#include     
#include    
#include 

#define MCAST_PORT 		8888
#define MCAST_ADDR 		"224.0.0.100"    /*一个局部连接多播地址,路由器不进行转发*/
#define MCAST_DATA 		"BROADCAST TEST DATA11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111zz"            /*多播发送的数据*/
#define MCAST_INTERVAL 	1                            /*发送间隔时间*/
int main(int argc, char*argv)
{
    int s;
    struct sockaddr_in mcast_addr;     
    s = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/
    char buf[1024];
    int i = 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);        /*设置多播端口*/
   
    memset(buf,0,sizeof(buf));
    buf[0]='a';
    buf[1]='a';
    buf[2]='a';
    buf[3]='a';
    for(i=4;i0) {
    while(1) {
        int n = sendto(s,                           /*套接字描述符*/
                                    buf,     /*数据*/
                                    sizeof(buf),    /*长度*/
                                    0,
                                    (struct sockaddr*)&mcast_addr,
                                    sizeof(mcast_addr)) ;
		printf("sendto n=%d\n",n);
        if( n < 0)
        {
            perror("sendto()");
            return -2;
        }  

        printf("sendto n=%d\n",n); 
		//count --;   
       
        usleep(1000);                          /*等待一段时间*/
    }
   
    return 0;
}

net_rate.c


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define DTV_CONFIG_ETH0_NET_INTERFACE "eno1"
#define DTV_CONFIG_LO_NET_INTERFACE "lo"
#define SERVICE_NET_DEFAULT_GET_NET_RATE_SECOND (5)

typedef int caoft_bool;
#ifndef NULL
            #define NULL             0L
#endif
#ifndef true
	#define true 1
#endif
#ifndef false
	#define false 0
#endif

typedef char caoft_s8;
typedef unsigned char caoft_u8;
typedef signed short caoft_s16;
typedef unsigned short caoft_u16;
typedef signed int caoft_s32;
typedef unsigned int caoft_u32;

typedef  long long	caoft_s64;
typedef  unsigned long long caoft_u64;

typedef long unsigned int	caoft_size_t;

#undef INVALID_HANDLE
#define INVALID_HANDLE		(0xFFFFFFFF)
	
#undef INVALID_VALUE
#define INVALID_VALUE		(0xFFFFFFFF)

typedef caoft_s32 caoft_time_t; 
	
#define caoft_UNUSED(x) ((x)=(x))

typedef void * caoft_phandle;
typedef caoft_u32  caoft_uhandle;    
typedef caoft_s32  caoft_shandle;

typedef enum{
    caoft_SUCCESS = 0,/** success HI_3G_CARD_SUCCESS*/
    caoft_FAILURE, 
    caoft_NOT_INITIALIZED,/**HI_3G_CARD_ERR_NOINIT error code for card not init */
    caoft_INVALID_PARAMETER,/**HI_3G_CARD_ERR_INVAL error code for invalid args */
    caoft_NOT_SUPPORT,/** HI_3G_CARD_ERR_ENOCARD error code for no support card when scan */
}caoft_error_code_e;

/*************************************************************************
Description
	s级的sleep
Definition
	void caoft_os_sleep(caoft_s32 s);
Arguments
	s:秒数
Return Value
	无
Comments
	无
See Also
	无

*************************************************************************/
void caoft_os_sleep(caoft_u32 s)
{
    sleep(s);
}
/*************************************************************************
Description
	文件打开操作

Definition
	caoft_phandle caoft_fs_file_open(const caoft_s8* path,const caoft_s8 * mode);

Arguments
	path:文件路径
	mode:打开模式,如"r+"
Return Value
	true:failure
	false:sucess
Comments
	无
See Also	
	无
*************************************************************************/
caoft_phandle caoft_fs_file_open(const caoft_s8* path,const caoft_s8 * mode)
{	
    FILE *file=NULL;

    if((NULL==path)||(NULL == mode))
    {
        printf(" path mode NULL\n");
        return NULL;
    }
    file=fopen((const char *)path, (const char *)mode);	
    if(NULL==file)
    {
        printf("fopen failed,path:[%s],mode=[%s]\t error: %s!\n", path, mode, strerror(errno));
        return NULL;
    }
    
    return (caoft_phandle)file;
}

/*************************************************************************
Description
文件关闭操作

Definition
	caoft_bool caoft_fs_file_close(caoft_phandle handle)

Arguments
	handle:文件句柄

Return Value
	true:failure
	false:sucess
Comments
	无
See Also
	无

*************************************************************************/

caoft_bool caoft_fs_file_close(caoft_phandle handle)
{
    if(0==handle)
    {
        printf(" handle NULL\n");
        return true;
    }
    fclose((FILE *)handle);	
    return false;
}

/*获取网口上行下行网速*/
static caoft_error_code_e service_net_get_current_flow(caoft_s8 *netname,caoft_u64 *rcv_flow,caoft_u64 *tx_flow)
{  				 
	FILE * net_dev_file;	//文件指针
	caoft_s8 buffer[1024];	//文件中的内容暂存在字符缓冲区里
	caoft_s8 * match;	 //用以保存所匹配字符串及之后的内容
	caoft_u32 counter = 0;//用以计算tx 发送字节数的位置
	caoft_u32 i = 0;
	caoft_s8 tmp_value[128];//偏移数据数值使用

	if((NULL == netname)||(NULL == rcv_flow)||(NULL == tx_flow))
	{
		printf("bad param\n");
		return caoft_INVALID_PARAMETER;	  
	}

	if ( (net_dev_file=caoft_fs_file_open("/proc/net/dev", "r")) == NULL ) //打开文件/pro/net/dev/,我们要读取的数据就是它啦
	{
		printf("caoft_fs_file_open /proc/net/dev/ failed!\n");
		return caoft_OPEN_FAILED;
	}
	memset(buffer,0,sizeof(buffer));

	while(fgets(buffer,sizeof(buffer),net_dev_file) != NULL)
	{
			match = strstr(buffer,netname);

			if(NULL == match)
			{
				//service_net_debug_info("no %s keyword to find!\n",netname);
				continue;
			}
			else
			{
				//service_net_debug_info("%s\n",buffer);
				match = match + strlen(netname) + strlen(":");/*地址偏移到冒号*/
				sscanf(match,"%llu ",rcv_flow);
				memset(tmp_value,0,sizeof(tmp_value));
				sscanf(match,"%s ",tmp_value);
				match = match + strlen(tmp_value);
				counter = 0;
				
				for(i=0;i

指定网卡发送组播

#include
#include  
#include     
#include      
#include    
#include     
#include    
#include
#include 
#include 
#include 

#define UDP_ADDR "239.255.255.250"
#define UDP_PORT 3702
#define SEND_PORT 3702
#define MAC_SIZE    18
#define IP_SIZE     16
#define ETHX     "enp0s31f6.20"

//获取网卡对应IP地址[其中eth是网卡名字 如“eth0” “eth1”等,ipaddr是输出参数]
// 获取本机ip
int get_local_ip(const char *eth_inf, char *ip)
{
    int sd;
    struct sockaddr_in sin;
    struct ifreq ifr;

    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sd)
    {
        printf("socket error: %s\n", strerror(errno));
        return -1;
    }

    strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    // if error: No such device
    if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
    {
        printf("ioctl error: %s\n", strerror(errno));
        close(sd);
        return -1;
    }

    memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
    snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

    close(sd);
    return 0;
}

int main(int argc, char **argv)
{

    struct sockaddr_in addr, mcast_addr;
    int fd;
    struct ip_mreq ipmr;
    char ip[IP_SIZE];
    //char mac_addr[MAC_SIZE];
    //char *mac_addr = {0};
    int ret = -1;
    //char msg[8];

    unsigned char chrUDP[124] = { '\x56','\x5a','\x00','\x00','\x02','\x01',
                                  '\x09' ,'\x05','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x03' ,'\x7e',
                                  '\xc9' ,'\x77','\xfc','\xf6','\xeb','\x14','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00' ,'\x00',
                                  '\x00' ,'\x00','\x00','\x00','\x00','\x00','\x7c','\x00','\x00','\x00','\xc0','\xa8','\x0a','\x64','\x50' ,'\x00',
                                  '\x00' ,'\x00','\x53','\x56','\x5a','\x54','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00' ,'\x00',
                                  '\x00' ,'\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00' ,'\x00',
                                  '\x00' ,'\x00','\x00','\x00','\x00','\x00','\x0b','\x00','\x00','\x00','\x2e','\x00','\x00','\x00','\x00' ,'\x00',
                                  '\x00' ,'\x00','\x01','\x00','\x00','\x0d','\x0d','\x00','\x00','\x00','\xc0','\xa8','\x0a','\x01','\xff' ,'\xff',
                                  '\xff' ,'\x00','\x72','\x00','\x00','\x00'};
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("create socket failed!");
        return -1;
    }

    //get_local_mac(ETHX, mac_addr);
    //printf("local %s mac: %s\n", ETHX, mac_addr);

    get_local_ip(ETHX, ip);
    printf("local %s ip: %s\n", ETHX, ip);
    inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);


    memset(&addr,0,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SEND_PORT);
    //addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_addr.s_addr = inet_addr(ip);

    ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0)
    {
        printf("bind error!!!");
        perror("bind:");
        close(fd);
        return -1;
    }

/**********************************************************************************************
**将本地socket添加到多播组中,注意,此处针对struct ip_mreq结构体需要填充两个成员,
**成员ipmr.imr_interface.s_addr的值指定的是将要发送的网卡的ip地址,
**成员impr.imr_multiaddr指定的是组播地址;
**如果指定为INADDR_ANY则系统会绑定一个默认网卡的具体ip(根据默认网关选择),则会出现特定网卡可以发送和接收组播信息,
**另一网卡不可以。即指定INADDR_ANY并不能把所有网卡都添加多播组中,必须明确指定对应网卡ip才可以。
***********************************************************************************************/

    struct in_addr inaddr = {0};
    inaddr.s_addr = inet_addr(ip);
    ipmr.imr_interface.s_addr = inaddr.s_addr;
    ipmr.imr_multiaddr.s_addr = inet_addr(UDP_ADDR);
    ret=setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&ipmr, sizeof(ipmr));


    /*此处指定组播数据的出口网卡,如果不设置则会根据路由表指定默认路由出口*/

    if(-1 == setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&inaddr, sizeof(inaddr)))
    {
        printf("set error IP_MULTICAST_IF %s\n", ip);
        perror("Setting IP_MULTICAST_IF error:");
        close(fd);
        fd = -1;
    }


    memset(&mcast_addr, 0, sizeof(mcast_addr));
    mcast_addr.sin_family = AF_INET;
    mcast_addr.sin_addr.s_addr = inet_addr(UDP_ADDR);
    mcast_addr.sin_port = htons(UDP_PORT);

    while(1){
        if (sendto(fd, (const char *)chrUDP, sizeof(chrUDP), 0, (struct sockaddr *)&mcast_addr, sizeof(mcast_addr)) < 0)
        {
            perror("sendto");
            return -1;
        }
        printf("send ok!\n");

        sleep(1);
    }

    setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mcast_addr, sizeof(mcast_addr));
    close(fd);

    return 0;
}

 

你可能感兴趣的:(tcp/ip详解,网络,tcp/ip,p2p)