由于公司业务需要用到组播实现,这里就记录下学习过程。在学习组播之前,我们先来看看另外两种数据包传输方式:单播和广播。
组播(Multicast),又称“多播”,是一种数据包传输方式。它以"尽力而为"的形式发送信息到某个目标组,这个目标组称为组播组。
源主机向多个主机发送数据时,源主机只发送一份数据,数据的目的地址是组播组地址。这样,凡是属于该组的成员,都可以接收到一份源主机发送的数据的拷贝,此组播方式下,只有真正信息需要的成员会收到信息,其他主机不会收到。
组播相较于单播和广播的优势:
小结:当有多台主机同时成为一个数据包的接受者时,出于对带宽和CPU负担的考虑,组播成为了一种最佳选择。
组播通过把224.0.0.0-239.255.255.255的D类地址作为目的地址,有一台源主机发出目的地址是以上范围组播地址的报文,在网络中,如果有其他主机对于这个组的报文有兴趣的,可以申请加入这个组,并可以接受这个组,而其他不是这个组的成员是无法接受到这个组的报文的。
上面说到了组播路由器,这里我们着重看下这个组播路由器的作用。
用户根据IGMP协议发送请求报文,路由器收到IGMP报文后,会把用户加入自己的组播组,组播报文到达路由器时,根据组播组复制多份数据发给组内的所有主机。
注意:IGMP报文并不是发给路由器,它的目的地址只有目标主机,报文从用户到目标主机可能经历多个路由器,用户必须加入这些路由器的组播组,为什么呢?因为只有用户加入了这条路径上所有的路由器的组播组之后,组播源发出的数据,才能在经过层层路由是转发到正确的目标用户。
发送IGMP报文需要知道组播源的IP地址,那用户是如何知道组播源的IP地址的呢?答案是:RP(Rendezvous Point)集中点,具体来说就是,让组播源知道RP的IP地址,让用户知道RP的IP地址。
MulticastServer.h
#include
#include
#include
#include
#include
#include
#include
#include #define SERVER_PORT 8000
#define CLIENT_PORT 9000
#define GROUP "239.0.0.2"using namespace std;class MulticastServer{
public:MulticastServer();~MulticastServer();bool Init();void SendMessage(string payloadMessage);private:int m_sockfd;struct sockaddr_in m_serveraddr, m_clientaddr;
};
MulticastServer.cpp
#include "MulticastServer.h"bool MulticastServer::Init()
{m_sockfd = socket(AF_INET, SOCK_DGRAM, 0); /*构造用于UDP通信的套接字*/bzero(&m_serveraddr, sizeof(m_serveraddr));m_serveraddr.sin_family = AF_INET; /* IPv4 */m_serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /*本地任意IP INADDR_ANY = 0 */m_serveraddr.sin_port = htons(SERVER_PORT);bind(m_sockfd, (struct sockaddr *)&m_serveraddr, sizeof(m_serveraddr));struct ip_mreqn group;inet_pton(AF_INET, GROUP, &group.imr_multiaddr); /*设置组播组的地址*/inet_pton(AF_INET, "0.0.0.0", &group.imr_address); /* 本地任意IP 自动分配有效IP*/group.imr_ifindex = if_nametoindex("enp5s0"); /* 给出网卡名,转换为对应编号:eth0 --> 编号 ,, 命令:ip ad */int ret = setsockopt(m_sockfd, IPPROTO_IP, IP_MULTICAST_IF, &group, sizeof(group)); /*获取组播权限*/if (ret < 0) {printf("Fail to disable multicast loop, err: %s",strerror(errno));return false;}else{printf("disable multicast loop success.\n");}// ret = setsockopt(m_sockfd, IPPROTO_IP , IP_MULTICAST_LOOP, &group, sizeof(group));bzero(&m_clientaddr, sizeof(m_clientaddr)); /* 构造client 地址 IP+端口号*/m_clientaddr.sin_family = AF_INET;inet_pton(AF_INET, GROUP, &m_clientaddr.sin_addr.s_addr); /* IPv4 239.0.0.2+9000 */m_clientaddr.sin_port = htons(CLIENT_PORT);return true;
}void MulticastServer::SendMessage(string payloadMessage)
{// sprintf(buf, "from 192.168.60.213 server info: multicast %d\n", i++);//fgets(buf, sizeof(buf), stdin);sendto(m_sockfd, (char*)payloadMessage.c_str(), payloadMessage.size(), 0, (struct sockaddr *)&m_clientaddr, sizeof(m_clientaddr));
}
main.c
#include "MulticastServer.h"int main(int argc, char *argv[])
{MultiBroadcastServer server;server.init();int idx = 0;while(true){idx++;std::string msg = "from 192.168.60.213 server info: multicast " + to_string(idx) + "\n";server.SendMessage(msg);sleep(1);}return 0;
}
MulticastClient.h
#include
#include
#include
#include
#include
#include
#include
#include #define SERVER_PORT 8000
#define CLIENT_PORT 9000
#define GROUP "239.0.0.2"using namespace std;class MulticastClient{
public:MulticastClient();~MulticastClient();bool Init();void recvMessage(char* buffer, int &len);private:int m_confd;struct sockaddr_in m_clientaddr;
};
MulticastClient.cpp
#include "MulticastClient.h"bool MulticastClient::Init()
{struct ip_mreqn group; /*组播结构体*/m_confd= socket(AF_INET, SOCK_DGRAM, 0);bzero(&m_clientaddr, sizeof(m_clientaddr)); /* 初始化*/m_clientaddr.sin_family = AF_INET;inet_pton(AF_INET, "0.0.0.0" , &m_clientaddr.sin_addr.s_addr);m_clientaddr.sin_port = htons(CLIENT_PORT);bind(m_confd, (struct sockaddr *)&m_clientaddr, sizeof(m_clientaddr));inet_pton(AF_INET, GROUP, &group.imr_multiaddr); /* 设置组播组地址*/inet_pton(AF_INET, "0.0.0.0", &group.imr_address); /*使用本地任意IP添加到组播组*/group.imr_ifindex = if_nametoindex("enp5s0"); /* 设置网卡名 编号 ip ad */ setsockopt(m_confd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group));/* 将client加入组播组*/return true;
}void MulticastClient::recvMessage(char* buffer, int &len)
{len = recvfrom(m_confd, buffer, sizeof(buffer), 0, NULL, 0);std::cout << "client recv: " << std::string(buffer) << " , len : "<< len << std:endl;
}
main.c
#include "MulticastClient.h"int main(int argc, char *argv[])
{MulticastClient client;client.init();while(true){char buffer[BUFSIZ] = {0};int len = 0;client.recvMessage(buffer, len);}return 0;
}