UDP的组播简单实现

第一篇博客,从今天开始,目的只是为自己做个备忘,感觉自己的记性越来越差了。ps现在只能一个手敲代码,好累好慢!

最近做一个项目需要用到组播,细节的把组播研究了下,写了个简单的代码,以供自己以后使用。

发送端

对于发送端来说,很简单,跟普通的udp通信没有区别,创建一个socket,然后sendto目的地址(组播地址+端口)就可以了。

需要注意的是,如果想固定发送端的发送端口,就需要用bind把socket同端口绑定。

如果需要在某个固定端口用udp接收接收端的回馈,也需要用bind绑定指定端口。

 

#ifndef __MULTICAST_SENDER_HPP__
#define __MULTICAST_SENDER_HPP__
#include "stdafx.h"

namespace multicast{
	class sender
	{
	public:
		sender(const char* ipaddr, short port)
		{
			int opval = 1;
			m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
			setsockopt(m_sock,SOL_SOCKET,SO_REUSEADDR,(const char*)&opval, sizeof(int));
			memset(&m_multicast_addr, 0, sizeof(sockaddr_in));
			m_multicast_addr.sin_family = AF_INET;
			m_multicast_addr.sin_addr.s_addr = inet_addr(ipaddr);
			m_multicast_addr.sin_port = htons(port);
			memset(m_buffer, 0, sizeof(m_buffer));

			sockaddr_in local_addr;
			local_addr.sin_family = AF_INET;
			local_addr.sin_port = htons(port);
			local_addr.sin_addr.s_addr = INADDR_ANY;
			bind(m_sock, (sockaddr*)&local_addr, sizeof(sockaddr_in));

			ADDRINFO ai_hints,*ai_rval;
			struct sockaddr_in listen_addr, dest_addr;
			memset(&ai_hints, 0, sizeof(ai_hints));
			ai_hints.ai_family = AF_UNSPEC;
			ai_hints.ai_socktype = SOCK_DGRAM;
			ai_hints.ai_protocol = 0;
			ai_hints.ai_flags = AI_NUMERICSERV;
			memset(&ai_rval, 0, sizeof(ADDRINFO));
			getaddrinfo(ipaddr, "50000", &ai_hints, &ai_rval);
			memcpy(&listen_addr,ai_rval->ai_addr,ai_rval->ai_addrlen);
			printf("multicast address:%s:%d\n",inet_ntoa(listen_addr.sin_addr),htons(listen_addr.sin_port));

			//memset(&ai_hints, 0, sizeof(ai_hints));
			//ai_hints.ai_family = listen_addr.sin_family;
			//ai_hints.ai_socktype = SOCK_DGRAM;
			//ai_hints.ai_protocol = 0;
			//ai_hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
			//getaddrinfo(NULL, "50002", &ai_hints, &ai_rval);
			//memcpy(&dest_addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
			//char *addr = inet_ntoa(dest_addr.sin_addr);
		}
		~sender(){ closesocket(m_sock); }

		int send_msg(const char* msg, int len)
		{
			int ret = sendto(m_sock, msg, len, 0, (sockaddr*)&m_multicast_addr, sizeof(m_multicast_addr));
			return ret;
		}

	private:
		SOCKET m_sock;
		sockaddr_in m_multicast_addr;
		char m_buffer[1024 * 8];
	};
}

#endif

 

接收端

接收端稍微复杂一点。整体流程:创建socket->bind绑定多播的端口->加入多播组->接收消息。

之所以需要bind是因为需要在广播的端口接收数据。

如果需要给发送端回馈结果,可以从recvfrom中获取发送端的地址和端口,然后用户UDP发送过去就行。

说明一点,用bind绑定到组播的接口后,所有发送到这个端口的udp都能接收到,不单单只有组播的信息。

 

#ifndef __MULTICAST_RECEIVER_HPP__
#define __MULTICAST_RECEIVER_HPP__
#include "stdafx.h"
#include <ws2ipdef.h>

namespace multicast{
	class receiver
	{
	public:
		receiver(const char* ipaddr, int port)
		{
			if (ipaddr)
			{
				int opval = 1;
				m_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
				setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&opval, sizeof(int));
				m_multi_addr = ipaddr;
				m_multicast_addr.sin_family = AF_INET;
				m_multicast_addr.sin_addr.s_addr = inet_addr(ipaddr);

				sockaddr_in local_addr;
				local_addr.sin_family = AF_INET;
				local_addr.sin_port = htons(port);
				local_addr.sin_addr.s_addr = /*inet_addr("192.168.6.226")*/INADDR_ANY;
				bind(m_sock, (sockaddr*)&local_addr, sizeof(sockaddr_in));

				IP_MREQ mreq;
				mreq.imr_multiaddr.s_addr = inet_addr(ipaddr);
				mreq.imr_interface.s_addr = INADDR_ANY;
				setsockopt(m_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq));
				int loop = 1;
				setsockopt(m_sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loop, sizeof(loop));

				ADDRINFO ai_hints, *ai_rval;
				struct sockaddr_in listen_addr, dest_addr;
				memset(&ai_hints, 0, sizeof(ai_hints));
				ai_hints.ai_family = AF_UNSPEC;
				ai_hints.ai_socktype = SOCK_DGRAM;
				ai_hints.ai_protocol = 0;
				ai_hints.ai_flags = AI_NUMERICSERV;
				memset(&ai_rval, 0, sizeof(ADDRINFO));
				getaddrinfo(ipaddr, "50000", &ai_hints, &ai_rval);
				memcpy(&listen_addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
				printf("multicast address:%s:%d\n", inet_ntoa(listen_addr.sin_addr), htons(listen_addr.sin_port));
			}
		}
		~receiver(){
			IP_MREQ mreq;
			mreq.imr_multiaddr.s_addr = inet_addr(m_multi_addr.c_str());
			mreq.imr_interface.s_addr = INADDR_ANY;
			setsockopt(m_sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*)&mreq, sizeof(mreq));
			closesocket(m_sock);
		}

		int recv_msg(char* buffer,int len)
		{
			sockaddr_in from_addr;
			int fromlen = sizeof(from_addr);
			int ret = recvfrom(m_sock, buffer, len, 0, (sockaddr*)&from_addr, &fromlen);
			if (ret > 0)
			{
				printf("%s:%d\n",inet_ntoa(from_addr.sin_addr),ntohs(from_addr.sin_port));
				char temp[1024] = { 0 };
				sprintf(temp, "response msg");
				//ret = sendto(m_sock, temp, 1024, 0, (sockaddr*)&from_addr, fromlen);
			}
			return ret;
		}

	private:
		SOCKET m_sock;
		//224.0.0.1-239.255.255.255 组播地址范围
		std::string m_multi_addr;
		sockaddr_in m_multicast_addr;
	};
}

#endif

ps:部分代码是多余的,用来验证信息的,无视之

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值