前言
组播又称多目标广播、多播。网络中使用的一种传输方式,它允许把 所发消息传送给所有可能目的地中的一个经过选择的子集,即向明确指出的多种地址输送信息。是一种在一个发送者和多个接收者之间进行通信的方法。与任播(anycast)和单播(unicast)一起,组播也是一种IPv6的包传送方式。
一、组播IP简介?
IP地址
组播IP地址用于标识一个IP组播组。IANA(internet assigned number authority)把D类地址空间分配给IP组播,其范围是从224.0.0.0到239.255.255.255。如二进制表示,IP组播地址前四位均为1110八位组⑴ 八位组⑵ 八位组⑶ 八位组⑷1110
XXXX XXXXXXXX XXXXXXXX XXXXXXXX组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
二、组播源码
1.组播发送端
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
//建立套接字
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //IPV4,数据报套接字类型,不指定协议
//声明将连接地址
struct sockaddr_in srvaddr,srvaddr_loacl;
srvaddr_loacl.sin_family = AF_INET, //协议类型IPV4
srvaddr_loacl.sin_port = htons(44444), //端口号-网络字节序
srvaddr_loacl.sin_addr.s_addr = inet_addr("224.2.2.2"); //IP地址-字符串转IP
bind(sock_fd,(const struct sockaddr*)&srvaddr_loacl,sizeof(srvaddr_loacl));
srvaddr.sin_family = AF_INET, //协议类型IPV4
srvaddr.sin_port = htons(55555), //端口号-网络字节序
srvaddr.sin_addr.s_addr = inet_addr("224.2.2.2"); //IP地址-字符串转IP
struct ip_mreq ipMreq;
setsockopt(sock_fd, IPPROTO_IP, IP_MULTICAST_IF, (void*)&srvaddr,
sizeof(struct sockaddr_in));
ipMreq.imr_interface.s_addr = inet_addr("192.168.13.80");
ipMreq.imr_multiaddr.s_addr = inet_addr("224.2.2.2");
//setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipMreq, sizeof(ipMreq));
//发送数据
char buf[88] = { 0 };
int count = 0;
while (1)
{
memset(buf,0, 88);
sprintf(buf, "%d",count);
printf("udp client send:%s\n",buf);
sendto(sock_fd, buf, strlen(buf), 0, (const struct sockaddr*)&srvaddr, sizeof(srvaddr));
count++;
sleep(1);
}
//关闭套接字
close(sock_fd);
return 0;
}
在Ubuntu环境下使用Gcc编译
gcc xxx.c -o xxx.exe
2.接收端源码
代码如下(示例):
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define BUF_SIZE 88
#define PORT 55555 //端口号
#define ADDR "192.168.13.80" //设置本地IP地址
#define MUNICASTADDR "224.2.2.2" //组播IP
int main(int argc, char *argv[])
{
//建立套接字
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); //IPV4,数据报套接字类型,不指定协议
//说明本服务器地址
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET, //协议类型IPV4
srvaddr.sin_port = htons(PORT), //端口号-网络字节序
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); //IP地址-任意地址
//绑定地址
bind(sock_fd, (const struct sockaddr *)&srvaddr, sizeof(srvaddr));
//srvaddr.sin_addr.s_addr = inet_addr(ADDR);
setsockopt(sock_fd, IPPROTO_IP, IP_MULTICAST_IF, (void*)&srvaddr,sizeof(struct sockaddr_in));
struct ip_mreq ipMreq;
ipMreq.imr_interface.s_addr = inet_addr(ADDR); // 使本地IP地址加入组播
ipMreq.imr_multiaddr.s_addr = inet_addr(MUNICASTADDR); //使组播IP加入组播
setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipMreq, sizeof(ipMreq));/*组播接收端一定要设置*/
//接收数据
char buf[BUF_SIZE] = { 0 };
struct sockaddr_in cliaddr;
socklen_t cliaddrlen = sizeof(cliaddr);
memset(&cliaddr,0,sizeof(cliaddr)); //清零初始化
while (1)
{
bzero(buf, BUF_SIZE); //清零初始化
bzero(&cliaddr, cliaddrlen); //清零初始化
recvfrom(sock_fd, buf, BUF_SIZE, 0, (struct sockaddr *)&cliaddr, &cliaddrlen);
printf("udp server from [%s - %hu]:%s\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port,buf);
}
//关闭套接字
close(sock_fd);
return 0;
}
总结
bind函数解析
#include<sys/socket.h>
int bind(int sockfd, (const struct sockaddr*)&, socklen_t addrlen);
第一个参数作用:bind函数把一个本地协议地址赋予一个套接字。
第二个参数作用:是一个指向特定协议的地址结构的指针。
第三个参数作用:地址结构的长度。
ipMreq函数解析
struct ip_mreq ipMreq;
ipMreq.imr_interface.s_addr = inet_addr(ADDR); // 使本地IP地址加入组播
ipMreq.imr_multiaddr.s_addr = inet_addr(MUNICASTADDR); //使组播IP加入组播
作用:声明一个ip_mreq的结构体变量,用于加入组播。
setsockopt函数解析
setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipMreq, sizeof(ipMreq));/*组播一定要写接收*/
第一个参数作用:把本地设置赋值给套接字(sock_fd)。
第二个参数作用:引入协议包(IPPROTO_IP)。
第三个参数作用:加入一个组播。
第四个参数作用:指向设置设置组播的结构体。
第五个参数作用:地址结构的长度。