igmp组播实验 @yx

发布于:2023-01-09 ⋅ 阅读:(246) ⋅ 点赞:(0)

环境

window10 gns3

gns3安装

放一个别人的链接,blog里面写的很详细,应该怎么安装gns3环境。也有打包的资源。

安装gns

不过不需要完全安装。百度云下载速度我只想吐槽。所以我们只需要下载gns3和一个cisco路由器的镜像,比如c2691,使用本地作为gns的server。

网络拓扑

组播拓扑
cloud 是用来连接本地的虚拟环回口,可以查看网络管理,网络适配器是否存在loop口。没有的话,百度一下!
我添加了两个环回口,并配置了ip地址。

路由器配置

enable
//进入配置模式
config t
//开启组播
ip multicast-routing
//配置结构f0/0
interface FastEthernet 0/0
ip address 192.168.10.1 255.255.255.0
//使用igmp dm 模式
ip pim dense-mode

interface FastEthernet 0/1
ip address 192.168.20.1 255.255.255.0
ip pim dense-mode

到这一步组播网络就已经弄好了。

模拟组播

我们需要一个组播源和一个加入组播的成员。
在网上找了一下,发现了VLC media player 可以作为组播源,推送rtp流。使用一个VLC推流,本地一个VLC接收流能够播放MP4,但是wireshark没有抓到包。不知道从那个网卡出去了。看网上说通过配置路由表可以指定出口网卡。然而并没有用。如果有知道的朋友麻烦留言告知一下。
route -p add 224.1.1.4 mask 255.0.0.0 192.168.10.1 metric 3

使用c++程序模拟组播源和接受者

需要改一哈本机地址编译。

组播源
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

static const auto LOCAL_IP = "192.168.10.2";
static const auto MULTICAST_GROUP_ADDRESS = "239.255.43.21";
static const unsigned short MULTICAST_GROUP_PORT = 45454;
static const int MSGBUFSIZE = 1024;

int init_winsock()
{
	int iResult;
	WSADATA wsaData;

	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	return 0;
}

int test_mcast_send()
{
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd) {
		printf("socket error!!!\n");
		perror("socket:");
		return -1;
	}
	int ttl = 60; /* max = 255 */
	//int aaret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, (char*)&ttl, sizeof(ttl));
	int aaret = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl));  //设置组播TT
	if (-1 == aaret) {
		printf("IP_TTL error!!!\n");
		perror("setsockopt:");
		closesocket(sockfd);
		return -1;
	}

	int reuse = 1;
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0) {
		perror("Setting SO_REUSEADDR error");
		closesocket(sockfd);
		return -1;
	}

	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(MULTICAST_GROUP_PORT);
	addr.sin_addr.s_addr = INADDR_ANY;
	int addr_len = sizeof(addr);
	int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr));
	if (-1 == ret) {
		printf("bind localaddr error!!!\n");
		perror("bind:");
		closesocket(sockfd);
		return -1;
	}

	unsigned long if_addr = inet_addr(LOCAL_IP);
	ret = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&if_addr, sizeof(if_addr));
	if (-1 == ret) {
		printf("IP_MULTICAST_IF error!!!\n");
		perror("setsockopt:");
		closesocket(sockfd);
		return -1;
	}

	char host[1024] = { 0 };
	gethostname(host, 1024);

	int msgNo = 0;
	char msg[1024] = { 0 };
	addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP_ADDRESS);
	while (true) {
		//sprintf(msg, "Groupcast Message %s No.%d", host, msgNo++);
		sprintf(msg, "ControlCenterMessage %d", msgNo++);
		int ret = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&addr, addr_len);
		if (ret < 0) {
			perror("sendto");
			return -1;
		}
		else {
			printf("Sent msg: %s\n", msg);
		}
		Sleep(1000);
	}

	return 0;
}

int main()
{
	int ret = init_winsock();
	if (0 != ret) { return ret; }

	ret = test_mcast_send();

	getchar();
}
接收者
//#include <Windows.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")

static const auto LOCAL_IP = "192.168.20.5";
static const auto MULTICAST_GROUP_ADDRESS = "239.255.43.21";
static const unsigned short MULTICAST_GROUP_PORT = 45454;
static const int MSGBUFSIZE = 1024;

int init_winsock()
{
	int iResult;
	WSADATA wsaData;

	// Initialize Winsock
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup failed: %d\n", iResult);
		return 1;
	}

	return 0;
}

int test_multicast_with_local_ip()
{
	int ret = 0;

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd) {
		printf("socket error!!!\n");
		perror("socket:");
		return -1;
	}

	int reuse = 1;
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse)) < 0) {
		perror("Setting SO_REUSEADDR error");
		closesocket(sockfd);
		return -1;
	}

	/*test ip*/
	struct sockaddr_in localaddr = { 0 };
	localaddr.sin_family = AF_INET;
	localaddr.sin_port = htons(MULTICAST_GROUP_PORT);
	localaddr.sin_addr.s_addr = /*inet_addr(LOCAL_IP)*/ htonl(INADDR_ANY);
	ret = bind(sockfd, (struct sockaddr*)&localaddr, sizeof(struct sockaddr));
	if (-1 == ret) {
		printf("bind localaddr error!!!\n");
		perror("bind:");
		closesocket(sockfd);
		return -1;
	}



	/*设置是否支持本地回环接收*/
	/*int loopBack = 1;
	ret = setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&loopBack, sizeof(loopBack));
	if (-1 == ret) {
	printf("setsockopt broadcaset error!!!\n");
	perror("setsockopt:");
	closesocket(sockfd);
	return -1;
	}*/

	/*
	将本地socket添加到多播组中,注意,此处针对struct ip_mreq结构体需要填充两个成员,
	成员ipmr.imr_interface.s_addr的值指定的是将要发送的网卡的ip地址,
	成员impr.imr_multiaddr指定的是组播地址;
	如果指定为INADDR_ANY则系统会绑定一个默认网卡的具体ip(根据默认网关选择),则会出现特定网卡可以发送和接收组播信息,另一网卡不可以。
	即指定INADDR_ANY并不能把所有网卡都添加多播组中,必须明确指定对应网卡ip才可以。*/
	//struct in_addr addr = { 0 };
	//addr.s_addr = inet_addr(local_ip);
	struct ip_mreq ipmr = { 0 };
	ipmr.imr_interface.s_addr = inet_addr(LOCAL_IP) /*(INADDR_ANY)*/;
	ipmr.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP_ADDRESS);
	int len = sizeof(ipmr);
	ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&ipmr, len);
	if (-1 == ret) {
		printf("set error IP_ADD_MEMBERSHIP %d\n", WSAGetLastError());
		perror("setsockopt:");
		closesocket(sockfd);
		return -1;
	}

	/*此处指定组播数据的出口网卡,如果不设置则会根据路由表指定默认路由出口*/
	/*if (-1 == setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&addr, sizeof(addr))) {
		printf("set error IP_MULTICAST_IF %s\n", local_ip);
		perror("Setting IP_MULTICAST_IF error:");
		closesocket(sockfd);
		sockfd = -1;
	}*/

	/*struct timeval tv;
	tv.tv_sec = 2;
	tv.tv_usec = 0;
	ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
	if (-1 == ret) {
		printf("setsockopt recvtimeout error!!!\n");
		perror("setsockopt:");
		closesocket(sockfd);
		return -1;
	}*/

	/* now just enter a read-print loop */
	char msgbuf[MSGBUFSIZE];
	int nbytes = 0;
	//localaddr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP_ADDRESS);
	localaddr.sin_addr.s_addr = inet_addr(LOCAL_IP);
	while (1) {
		int addrlen = sizeof(localaddr);
		if ((nbytes = recvfrom(sockfd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &localaddr, &addrlen)) < 0) {
			perror("recvfrom");
			return -1;
		}
		msgbuf[nbytes] = 0;
		puts(msgbuf);
	}

	return 0;
}

int test_multicast()
{
	int fd;
	/* create what looks like an ordinary UDP socket */
	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return(1);
	}

	/**** MODIFICATION TO ORIGINAL */
	/* allow multiple sockets to use the same PORT number */
	u_int yes = 1;            /*** MODIFICATION TO ORIGINAL */
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)) < 0) {
		perror("Reusing ADDR failed");
		return(1);
	}
	/*** END OF MODIFICATION TO ORIGINAL */

	/* set up destination address */
	struct sockaddr_in addr = {};
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY); /* N.B.: differs from sender */
	addr.sin_port = htons(MULTICAST_GROUP_PORT);

	/* bind to receive address */
	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
		perror("bind");
		return(1);
	}

	/* use setsockopt() to request that the kernel join a multicast group */
	struct ip_mreq mreq = {};
	mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP_ADDRESS);
	//mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0) {
		perror("IP_ADD_MEMBERSHIP");
		//return(1);
	}

	/* now just enter a read-print loop */
	int nbytes = 0;
	int addrlen = sizeof(addr);
	char msgbuf[MSGBUFSIZE] = {};
	while (1) {
		if ((nbytes = recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, &addrlen)) < 0) {
			perror("recvfrom");
			return(1);
		}
		msgbuf[nbytes] = 0;
		printf("receive:%s\n", msgbuf);
	}

	return 0;
}

int main()
{
	int ret = init_winsock();
	if (0 != ret) { return ret; }

	ret = test_multicast_with_local_ip();

	//ret = test_multicast();

	getchar();
}

实验结果查看

本来接收者可以直接打印出消息的。后面抓包发现,组播流被封装在了负载里面。所有只有抓包才能查看结果了。
在这里插入图片描述

遇到的问题

1:添加了环回口后,连接cloud和路由器报错:网卡打开失败.
解决:电脑没有安装winpcap。 或者以前安装过,下载不干净,导致安装失败。卸载干净后重新安装。
2:组播包到达路由器后没有被转发。
解决:网上搜到一篇写的很详细的排查过程。特别厉害。
igmp错误排查
我的问题是构建的组播报文ttl值为1.导致路由器直接将包丢弃了。修改组播报文ip.tll为60.


网站公告

今日签到

点亮在社区的每一天
去签到