第一篇博客,从今天开始,目的只是为自己做个备忘,感觉自己的记性越来越差了。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
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:部分代码是多余的,用来验证信息的,无视之