网络编程之多播

Posted oldmao_2000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程之多播相关的知识,希望对你有一定的参考价值。

文章目录

简介

单播数据只能发送给单个目的主机,一个发送方、一个接收方,两个主机间的通信不影响其他主机。广播数据是通过广播地址将数据发送给局域网上的所有主机,广播一般只在单个局域网中传播。
多播和广播一样也是将数据发送给多个主机,但仅将数据发送给属于相同多播组的主机,而且多播数据能通过路由器进行传播。

IGMP

多播使用的协议是IGMP(Internet Group Management Protocol),该协议是IP协议簇的一个组成部分,可在IP首部的协议字段设置为2,标记为IGMP协议。IGMP共有三个版本:IGMPv1、IGMPv2、IGMPv3。

版本加入多播组离开多播组多播源过滤
IGMPv1××
IGMPv2×
IGMPv3
多播源过滤是指可设置接收多播数据的主机白名单。这个功能是IGMPv3才有的,Windows Vista及以上版本才支持IGMPv3。

多播地址

多播地址属于D类IP地址,前4位为固定的1110,剩余的28位用于标识多播组。

前4位后28位
1110多播组标识
用十进制表示多播地址范围是:224.0.0.0~239.255.255.255。组播组可以是永久的也可以是临时的。
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为本地管理组播地址,仅在特定的本地范围内有效。
固定的多播组地址是是永久的,它们的组员个数在任意时刻是变化的,甚至是0个;其他临时多播组则只有组员个数大于0时才存在。

RFC官方规定了一些固定的多播组地址:

多播地址用途
224.0.0.0保留地址
224.0.0.1子网所有组播主机,含网关
224.0.0.2子网所有组播路由器
224.0.0.4DRMRP 路由器
224.0.0.5所有OSPF的路由器
224.0.0.6OSPF指定路由器
224.0.0.10EIGRP 路由器
224.0.0.12DHCP服务器/中继器
224.0.1.1NTP 网络时间协议

组播相关结构体

https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq

typedef struct ip_mreq 
  IN_ADDR imr_multiaddr;
  IN_ADDR imr_interface;
 IP_MREQ, *PIP_MREQ;

成员①:多播组IP
成员②:本地IP地址

typedef struct ip_mreq_source 
  IN_ADDR imr_multiaddr;
  IN_ADDR imr_sourceaddr;
  IN_ADDR imr_interface;
 IP_MREQ_SOURCE, *PIP_MREQ_SOURCE;

成员①:多播组IP
成员②:源地址黑/白名单
成员③:本地IP地址

多播实例

这里只有一台机器,但是仍然能够看出来和简单的单播不一样的地方,发送的目标是组播IP,接收的时候使用本地IP

发送

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<winsock2.h>
#include<ws2ipdef.h>
#include<stdio.h>

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

//项目->属性->高级,字符集选择【使用多字符集】
int main()

	WORD wVersionRequested = MAKEWORD(2, 2);//版本
	WSADATA wsaDATA;

	//打开网络库
	if (WSAStartup(wVersionRequested, &wsaDATA) != 0)
	
		printf("打开网络库失败!\\n");
		return -1;
		
	
	ip_mreq mreq;	
	int TTL = 8;

	mreq.imr_interface.S_un.S_addr = inet_addr("127.0.0.1");//本地IP
	mreq.imr_multiaddr.S_un.S_addr = inet_addr("234.2.3.4");//多播组IP
	
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//创建Socket句柄,多播只支持UDP

	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&TTL, sizeof(TTL)) != 0)//设置TTL
	
		printf("setsockopt设置TTL失败!\\n");
		closesocket(sock);//关闭Socket句柄
		WSACleanup();//关闭网络库
		return -1;
	

	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(ip_mreq)) != 0)//加入多播组
	
		printf("setsockopt设置TTL失败!\\n");
		closesocket(sock);//关闭Socket句柄
		WSACleanup();//关闭网络库
		return -1;
	

	struct sockaddr_in multiif;
	multiif.sin_family = AF_INET;
	multiif.sin_port = htons(9527);//用htons宏将整型转为端口号的无符号整型

	multiif.sin_addr.S_un.S_addr = inet_addr("234.2.3.4");//使用多播IP作为目标IP  
	//multiif.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//相当于绑定到0.0.0.0

	int i = 0;
	char Sendbuf[0x100] =  0 ;

	while (1)
	
		sprintf_s(Sendbuf,"%d", i);//将自增量写入发送变量
		i++;
		//也可以用connect+send组合
		if (sendto(sock, Sendbuf, sizeof(Sendbuf), 0, (const struct sockaddr*)&multiif, sizeof(sockaddr_in)) == SOCKET_ERROR)
		
			int err = WSAGetLastError();//取错误码
			printf("服务器sendto失败错误码为:%d\\n", err);
			continue;
		
		Sleep(1000);//休息1秒钟再发送
	
	

	closesocket(sock);//关闭Socket句柄
	WSACleanup();//关闭网络库
	return 0;

接收

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<winsock2.h>
#include<ws2ipdef.h>
#include<stdio.h>

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

int main()

	WORD wVersionRequested = MAKEWORD(2, 2);//版本
	WSADATA wsaDATA;

	//打开网络库
	if (WSAStartup(wVersionRequested, &wsaDATA) != 0)
	
		printf("打开网络库失败!\\n");
		return -1;
	

	ip_mreq mreq;
	int TTL = 8;

	mreq.imr_interface.S_un.S_addr = inet_addr("127.0.0.1");//本地IP
	mreq.imr_multiaddr.S_un.S_addr = inet_addr("234.2.3.4");//多播组IP

	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);//创建Socket句柄,多播只支持UDP

	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&TTL, sizeof(TTL)) != 0)//设置TTL
	
		printf("setsockopt设置TTL失败!\\n");
		closesocket(sock);//关闭Socket句柄
		WSACleanup();//关闭网络库
		return -1;
	

	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(ip_mreq)) != 0)//加入多播组
	
		printf("setsockopt设置TTL失败!\\n");
		closesocket(sock);//关闭Socket句柄
		WSACleanup();//关闭网络库
		return -1;
	

	struct sockaddr_in localif;
	localif.sin_family = AF_INET;
	localif.sin_port = htons(9527);//用htons宏将整型转为端口号的无符号整型

	localif.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//使用多播IP作为目标IP  

	if (SOCKET_ERROR == bind(sock, (const struct sockaddr*)&localif, sizeof(localif)))
	
		int err = WSAGetLastError();//取错误码
		printf("bind失败错误码为:%d\\n", err);
		closesocket(sock);//释放
		WSACleanup();//清理网络库

		return 0;
	

	char Recvbuf[0x100] =  0 ;
	int Recvlen = sizeof(localif);
	while (1)
	
		
		//用recv简单直接收取消息
		if (recv(sock, Recvbuf, sizeof(Recvbuf), 0) == SOCKET_ERROR)
		
			int err = WSAGetLastError();//取错误码
			printf("recv失败错误码为:%d\\n", err);
			continue;
		

		//也可以用recvfrom,额外获取发送方IP地址
		//struct sockaddr_in sa;
		//int iSaLen = sizeof(sa);
		//if (recvfrom(sock, Recvbuf, sizeof(Recvbuf), 0, (struct sockaddr*)&sa,&iSaLen) == SOCKET_ERROR)
		//
		//	int err = WSAGetLastError();//取错误码
		//	printf("recv失败错误码为:%d\\n", err);
		//	continue;
		//
		//printf("%s\\n", inet_ntoa(sa.sin_addr));


		printf("%s\\n", Recvbuf);
	

	closesocket(sock);//关闭Socket句柄
	WSACleanup();//关闭网络库
	return 0;

白名单多播源过滤法

使用ip_mreq_source 添加若干个多播源IP,形成白名单,不再接收其他多播源消息。setsockopt中的选项为IP_ADD_SOURCE_MEMBERSHIP

	ip_mreq_source mreqs;

	mreqs.imr_interface.S_un.S_addr = inet_addr("127.0.0.1");//本地IP
	mreqs.imr_multiaddr.S_un.S_addr = inet_addr("234.2.3.4");//多播组IP
	mreqs.imr_sourceaddr.S_un.S_addr = inet_addr("127.0.0.1");//白名单多播IP

	if (setsockopt(sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)&mreqs, sizeof(ip_mreq_source)) != 0)//加入多播组
	
		printf("setsockopt设置TTL失败!\\n");
		closesocket(sock);//关闭Socket句柄
		WSACleanup();//关闭网络库
		return -1;
	

如果要移除白名单中的多播IP,则setsockopt中的选项为IP_DROP_SOURCE_MEMBERSHIP

黑名单多播源过滤法

使用ip_mreq_source 添加若干个多播源IP,形成黑名单,不再接收黑名单中多播源消息。setsockopt中的选项为IP_ADD_BLOCK_SOURCE。
取消黑名单使用的选项为:IP_UNBLOCK_SOURCE
黑名单中的多播源需要存在多播组中,也就是先要用IP_ADD_MEMBERSHIP选项添加到多播组中的多播IP才能添加到黑名单中。

还有一种设置方式是使用WSAIoctl设置SIO_SET_MULTICAST_FILTER,该选项的数据类型如下:
https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_msfilter

typedef struct ip_msfilter 
  IN_ADDR             imsf_multiaddr;//多播地址
  IN_ADDR             imsf_interface;//本地地址
  MULTICAST_MODE_TYPE imsf_fmode;//黑名单或者白名单
  ULONG               imsf_numsrc;//名单成员数量
  IN_ADDR             imsf_slist[1];//名单具体成员
 IP_MSFILTER, *PIP_MSFILTER;

以上是关于网络编程之多播的主要内容,如果未能解决你的问题,请参考以下文章

计算机网络 网络层(下)

你真的了解IP地址吗?

计算机网络- TCP

计算机网络_传输层_基本概念

网络硬件7.IP多播的概念

网络硬件7.IP多播的概念