组播程序示例

小结:
1. 发送端也需要绑定网卡地址,目的是告诉协议栈,发送端的数据要往哪个子网“广播”。
2. 接收端bind绑定的地址与组播毫无关系,如果一个普通的udp发送端往接收端bind的地址值发数据,接收端也可以收到,也就是说,一个用组播的接收端,也可以收到单播的数据!
3. 对组播的一些理解:发送端绑定一个网卡地址(端口任意),目的地址为组播地址时,系统先把数据“广播”到与发送端的源地址所在子网的所有网卡上去,接收端的网卡收到了这个“广播”消息后,会检查该网卡是否加入了该组播组,如果是,则接收数据,否则丢弃数据。

4. 对于跨子网的组播传输,或许发送端不需要主动绑定网卡,但是当发送端调用send或者sendto时,系统依然会自动绑定第一块网卡,这就要求网络中的路由器能够找到一条路由,使得发送端的数据可以从发送端绑定的网卡经过此路由到达接收端的mreq.imr_interface上,这里的mreq.imr_interface指接收端加入组播组的网卡


发送端代码:

// multicast.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <iostream>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;
#define MULTI_PORT 8888
#define MULTI_GROUP "232.1.1.1"

int _tmain(int argc, _TCHAR* argv[])
{
    if(argc<2)
	{
	    cout<<"usage: receiver.exe interface"<<endl;
	}

	char buf[256];
	size_t len = wcslen(argv[1]) + 1;
	size_t converted = 0;
	wcstombs_s(&converted, buf, len, argv[1], _TRUNCATE);
	

	char *SRC_IP = buf;

	WSADATA wsad;
	WSAStartup(MAKEWORD(2,2),&wsad);

	char *message="Hello, World!";

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

	struct sockaddr_in addr;
	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_addr.s_addr=inet_addr(SRC_IP); 
	addr.sin_port=htons(0);

	// we have to bind souce ip, usually it is in the same subnet with receiver's 
	// ip_mreq.imr_interface. If you don't bind, the OS will bind it to the first NIC automatically,
	// which may cause receiver cannot receive data when receiver's ip_mreq.imr_interface != the first
	// NIC of sender. 
	if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0)
	{
		perror("bind");
		cout<<GetLastError()<<endl;
		exit(1);
	}

	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_addr.s_addr=inet_addr(MULTI_GROUP);
	addr.sin_port=htons(MULTI_PORT);

	/* now just sendto() our destination! */
	while (1)
	{
		if (sendto(fd,message, strlen(message), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0) 
		{
			perror("sendto");
			exit(1);
		}
		cout<<message<<endl;
		Sleep(2000);
	}

	return 0;
}


接收端代码:

// receiver.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stdafx.h"
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <iostream>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

#define MULTI_PORT  8888
#define MULTI_GROUP "232.1.1.1"
#define MSGBUFSIZE 256


int _tmain(int argc, _TCHAR* argv[])
{
    if(argc<2)
	{
	    cout<<"usage: receiver.exe interface"<<endl;
	}
	char buf[256];
	size_t len = wcslen(argv[1]) + 1;
	size_t converted = 0;
	//convert _TCHAR* to char*
	wcstombs_s(&converted, buf, len, argv[1], _TRUNCATE);
	char *LOCAL_IP = buf;

	WSADATA wsad;
	WSAStartup(MAKEWORD(2,2),&wsad);


	SOCKET fd;
	if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) 
	{
		perror("socket");
		exit(1);
	}

	//set address reuse
	int yes=1;
	if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&yes,sizeof(yes)) < 0) 
	{
		perror("Reusing ADDR failed");
		cout<<GetLastError()<<endl;
		exit(1);
	}

	//enable multicast loop. e.g. receive data when source ip==LOCAL_IP 
	//otherwise the data would be rejected when source ip==LOCAL_IP 
	yes = 1;
	if(setsockopt(fd,IPPROTO_IP,IP_MULTICAST_LOOP,(char*)&yes,sizeof(yes)) <0)
	{
		perror("set MULTICAST_LOOP");
		cout<<GetLastError()<<endl;
		exit(1);
	}

	//set ttl
	//int ttl=128;
	//if(setsockopt(fd,IPPROTO_IP,IP_MULTICAST_TTL,(char*)&ttl,sizeof(ttl)) <0)
	//{
	//	perror("set ttl");
	//	cout<<GetLastError()<<endl;
	//	exit(1);
	//}
	
	//bind socket
	struct sockaddr_in addr;
	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
    //here the bound ip INADDR_ANY or any other specific ip e.g. 10.35.19.195 has nothing to do with
	//multicast. Just like a common udp socket, it will receive the data if sender sends it to
	//INADDR_ANY or specific ip at port MULTI_PORT
    //this bind tell tcp stack that the receiver is interested in the data from which interface and which port
    //if we don't bind LOCAL_IP or INADDR_ANY, then tcp stack will drop the data directly sent to this interface. 
    //and this receiver will not receive multicast data.
    addr.sin_addr.s_addr=htonl(INADDR_ANY); 
	addr.sin_port=htons(MULTI_PORT);
	if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0)
	{
		perror("bind");
		cout<<GetLastError()<<endl;
		exit(1);
	} 

	struct ip_mreq mreq;
	memset(&mreq, 0, sizeof(mreq));
	//MULTI_GROUP: the multicast group you want to join
	mreq.imr_multiaddr.s_addr=inet_addr(MULTI_GROUP);
	//LOCAL_IP: which NIC to join the multicast group, it mean only NIC LOCAL_IP will monitor multicast 
	//data. Ususally the sender binds to a NIC which is in the same subnet with LOCAL_IP. And this ip should
	//be in the list of interfaces that have been bin
	mreq.imr_interface.s_addr = inet_addr(LOCAL_IP);
	
	//join muticast
	if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&mreq,sizeof(mreq)) < 0) 
	{
		perror("join multicast group");
		//cout<<GetLastError()<<endl;
		exit(1);
	}

	
	int nbytes,addrlen;
	char msgbuf[MSGBUFSIZE];
	while (1) 
	{
		addrlen=sizeof(addr);
		if ((nbytes=recvfrom(fd,msgbuf,MSGBUFSIZE,0, (struct sockaddr *) &addr, &addrlen)) < 0) 
		{
			perror("recvfrom");
			exit(1);
		}
		else
		{
			msgbuf[nbytes]=0;
			cout<<"from: "<<inet_ntoa(addr.sin_addr)<<" "<<nbytes<<": bytes: "<<(msgbuf)<<endl;
		}
	}

	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值