关于组播

Linux 网络编程 –多播(组播)编程

单播 && 广播:
单播用于两个主机之间的端对端的通信,
广播用于一个主机对整个局域网上所有主机上的数字通信。

单播和广播是两个极端,要么对一个主机进行通信,要么对整个局域网上的主机进行通信。实际情况下,经常需要对一组特定的主机进行通信,而不是整个局域网中的所有主机这就是多播的用途。

多播:
多播,也称为“组播”,将局域网中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。

多播的地址是特定的,D类地址用于多播。D类IP地址就是多播IP地址,即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址三大类。

局部多播地址:在224.0.0.0~224.0.0.255之间,这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

预留多播地址:在224.0.1.0~238.255.255.255之间,可用于全球范围(如Internet)或网络协议。

管理权限多播地址:在239.0.0.0~239.255.255.255之间,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

//属于永久组的地址:
224.0.0.1 所有组播主机
224.0.0.2 所有组播路由器
。。。
。。。

IP到以太网地址映射

因为以太网支持多种协议,所以要采取措施分配多播地址,避免冲突。IEEE管理以太网多播地址分配。
IEEE把一块以太网多播地址分给IANA以支持IP多播。块的地址都以01:00:5e开头。第25位为0,低23位为IPv4组播地址的低23位。IPv4组播地址与MAC地址的映射关系如下图所示:

。。。。。。
(图太难画了,截图在是手机中,理解为主)
D类IP地址到以太网多播地址的映射

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

多播主机

多播主机分为三个级别:
0级:主机不能发送或接收IP多播
这种主机应该自动丢弃它收到的具有D类目的地址的分组

1级:主机能发送但不能接收IP多播
在向某个IP多播组发送数据报之前,并不要求主机加入带该组。多播数据报的发送方式与单播一样,除了多播数据报的目的地址是IP多播组之外。网络驱动器必须能够识别出这个地址,在本地网络上多播数据报。

2级:主机能发送和接收IP多播
为了接收IP多播,主机必须能够加入或离开多播组,而且必须支持IGMP,能够在至少一个接口上交换组成员信息。多接口主机必须支持在它的接口的一个子网上的多播Net/3符合2级主机要求,可以完成多播路由器的工作。
与单播IP选路一样,我们假设所描述的系统是一个多播路由器,并加上了Net/3多播选路的程序。

Linux多播编程步骤:

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

Demo:
服务端:

#define _WINSOCK_DEPRECATED_NO_WARNINGS

/*
服务器端:
建立一个数据包套接字,选定多播的IP地址和端口,直接向此多播地址发送数据就可以了。多播服务器的程序设计,不需要服务器加入多播组,可以直接向某个多播组发送数据

下面的例子:
持续向多播IP地址"228.6.7.8"的7777端口发送数据MCAST_DATA,每次发送数据的时间间隔MCAST_INTERVAL
*/
//多端服务程序

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>

#define MCAST_PORT 7777
#define MCAST_ADDR “228.6.7.8” //一个局部连接多播地址,路由器不进行转发 这边定义为D类的IP地址
#define MCAST_DATA “hello world” //多播发送的数据
#define MCAST_INTERVAL 5 //发送间隔时间
#define BUFF_SIZE 256 //接收缓冲区大小

int main(int argc, char*argv[])

{
WSADATA wsaData;
int wsaRtn = WSAStartup(MAKEWORD(2, 2), &wsaData);

if (wsaRtn != 0)
{
	printf("WSAStartup() error!\n");
}

//建立socket
SOCKET s;
SOCKADDR_IN mcast_addr;  //socket使用参数结构体
s = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/
if (s == SOCKET_ERROR)
{
	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, (SOCKADDR*)&mcast_addr, sizeof(mcast_addr));
	if (n < 0)
	{
		perror("sendto()");
		return -2;
	}
	Sleep(MCAST_INTERVAL);

}
system("pause");
return 0;

}

客户端:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
/*
客户端:
多播组的IP地址为"228.6.7.8" ,端口为7777,当客户接收到多播的数据后,将数据打印出来

客户端只有在加入多播组后才能接收多播组的数据,
因此多播客户端在接收多播组的数据之前需要先加入多播组,当接收完毕后要退出多播组。

多播程序使用setsockopt()函数和getsockopt()函数实现,组播的选项是IP层的
1、选项IP_MULTICAST_TTL
允许设置超时TTL,范围为0~255之间的任何值,例如:
unsigned char ttl=255;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl));

2、选项IP_MULTICAST_IF
用于设置组播的默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据,例如:
struct in_addr addr;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr));
参数addr是希望多播输出接口的IP地址,使用INADDR_ANY地址回送到默认接口

3、选项IP_MULTICAST_LOOP
默认情况下,当本机发送组播数据到某个网络接口时,在IP层,数据会回送到本地的回环接口,
选项IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口。例如:
unsigned char loop;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));

4、选项IP_ADD_MEMBERSHIP 和IP_DROP_MEMBERSHIP
加入或者退出一个多播组,通过选项IP_ADD_MEMBERSHIP 和IP_DROP_MEMBERSHIP,对一个结构struct ip_mreq类型的变量进行控制,
struct ip_mreq原型如下:
struct ip_mreq
{
struct in_addr imn_multiaddr; //加入或退出多播组的IP地址
struct in_addr imr_interface; //加入或者退出的网络接口的IP地址
};
选项IP_ADD_MEMBERSHIP用于加入某个多组播,之后就可以向这个多组播发送数据或者从多播组接收数据。
此选项的值为mreq结构,成员imn_multiaddr是需要加入的多组播的IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。
例如:
struct ip_mreq mreq;
setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

*/
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <errno.h>
#include
using namespace std;

#define MCAST_PORT 7777
#define MCAST_ADDR “228.6.7.8” //一个局部连接多播地址,路由器不进行转发
#define MCAST_INTERVAL 5 //发送间隔时间
#define BUFF_SIZE 256 //接收缓冲区大小

int main(int argc, char*argv[])
{
WSADATA wsaData;
int wsaRtn = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (wsaRtn != 0)
{
printf(“WSAStartup() error!\n”);
}

SOCKET s;                             /*套接字文件描述符*/
SOCKADDR_IN local_addr;              /*本地地址*/
int err = -1;

/*1、建立套接字*/
s = socket(AF_INET, SOCK_DGRAM, 0);     /*建立套接字*/
if (s == SOCKET_ERROR)
{
	perror("socket()");
	return -1;
}

/*2、初始化本地地址
*/
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);

/*3、绑定socket*/
err = bind(s, (SOCKADDR*)&local_addr, sizeof(local_addr));
if (err == INVALID_SOCKET)
{
	perror("bind()");
	return -2;

}

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


/*5、加入多播组*/
ip_mreq mreq;                                   
mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); 
//htonl(s):将一个32位数从主机字节顺序转换为网络字节顺序
mreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本地ip地址接口
err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
if (err != 0)
{
	perror("setsockopt():IP_ADD_MEMBERSHIP");
	return -4;
}

/*6、接收多播组的消息*/
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, (SOCKADDR*)&local_addr,&addr_len);
	if (n == -1)
	{
		perror("recvfrom()");
	}
	printf("Recv %dst message from server:%s\n", times, buff);
	
	Sleep(MCAST_INTERVAL);
}

/*7、退出多播组*/
err = setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof
	(mreq));

closesocket(s);
system("pause");
return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值