目录

1.组播基本知识

2.组播编程实现

2.1 服务器端

2.2 客户端


1.组播基本知识

由于设计到多播通信方法,故查阅资料学习一下,三种通信方法:

类型

特点

优缺点

使用方法

单播         

用于两个主机之间的端对端通信

单播解决了点对点通信的需求;

TCP/UDP

广播

用于一个主机对整个局域网上所有主机上的数据通信

存在缺点:

1)只能在同一网段内实现广播;

2)不能指定目的主机,所有网段内的主机都将收到广播报文,存在带宽浪费。

UDP

多播

将局域网中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行

1.组播也是点对多点的通信,完全克服了广播的两个缺点,广泛应用实时性较高的信息服务。

2.可以局域网也可以是互联网

UDP

  D类地址用于多播。即224.0.0.0至239.255.255.255之间的IP地址,并被划分为局部连接多播地址、预留多播地址和管理权限多播地址3类:

类型

地址区间

特点

局部多播地址

224.0.0.0~224.0.0.255

这是为路由协议和其他用途保留的地址,路由器并不转发属于此范围的IP包。

预留多播地址

224.0.1.0~238.255.255.255

可用于全球范围(如Internet)或网络协议。

管理权限多播地址

239.0.0.0~239.255.255.255

可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制多播范围。

注意:TCP是基于面向连接的通信方式,只能点对点,故只能是单播

而UDP是传输之前不需要建议连接,可以是单播,多播和广播;

2.组播编程实现

多播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。参数如下:

宏定义

含义

示例

IP_MULTICAST_TTL

设置多播组数据的TTL值

选项IP_MULTICAST_TTL允许设置超时TTL,

范围为0~255之间的任何值,例如:

unsigned char ttl=255;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_TTL,&ttl,sizeof(ttl)); 

IP_ADD_MEMBERSHIP

在指定接口上加入组播组

加入或者退出一个多播组,通过选项IP_ADD_MEMBERSHIP和IP_DROP_MEMBER- SHIP,对一个结构struct ip_mreq类型的变量进行控制,struct ip_mreq原型如下:

struct ip_mreq          

    struct in_addr imn_multiaddr;      /*加入或者退出的广播组IP地址*/ 
  struct in_addr imr_interface;      /*加入或者退出的网络接口IP地址*/

};


 
选项IP_ADD_MEMBERSHIP用于加入某个多播组,之后就可以向这个多播组发送数据或者从多播组接收数据。此选项的值为mreq结构,成员imn_multiaddr是需要加入的多播组IP地址,成员imr_interface是本机需要加入广播组的网络接口IP地址。例如:

struct ip_mreq mreq;

setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

IP_DROP_MEMBERSHIP

退出组播组

IP_MULTICAST_IF

获取默认接口或设置接口

选项IP_MULTICAST_IF用于设置组播的默认默认网络接口,会从给定的网络接口发送,另一个网络接口会忽略此数据。例如:

struct in_addr addr;

setsockopt(s,IPPROTO_IP,IP_MULTICAST_IF,&addr,sizeof(addr));

IP_MULTICAST_LOOP

禁止组播数据回送

默认情况下,当本机发送组播数据到某个网络接口时,在IP层,数据会回送到本地的回环接口,选项IP_MULTICAST_LOOP用于控制数据是否回送到本地的回环接口。例如:

unsigned char loop;
setsockopt(s,IPPROTO_IP,IP_MULTICAST_LOOP,&loop,sizeof(loop));参数loop设置为0禁止回送,设置为1允许回送。

2.1 服务器端

 linux多播服务端编程思路:

1>建立一个socket;
2>设置多播的IP地址
3>发送和接收数据

服务器不需要加入多播组,可以直接向某个多播组发送数据。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define MCAST_PORT 8888;
#define MCAST_ADDR "224.0.0.100"/    
#define MCAST_DATA "BROADCAST TEST DATA"            
#define MCAST_INTERVAL 5                            
int main(int argc, char*argv)
{
    struct sockaddr_in mcast_addr;     
    int soketfd = socket(AF_INET, SOCK_DGRAM, 0);         /*建立套接字*/
    if (s == -1)
    {
        perror("socket()");
        return -1;
    }
 
    memset(&mcast_addr, 0, sizeof(mcast_addr));
    mcast_addr.sin_family = AF_INET;           
    mcast_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);
    mcast_addr.sin_port = htons(MCAST_PORT);    

                                               /*向多播地址发送数据*/
    while(1) {
        int n = sendto(soketfd,  MCAST_DATA,  sizeof(MCAST_DATA), 0, (struct sockaddr*)&mcast_addr,sizeof(mcast_addr)) ;
        if( n < 0)
        {
            perror("sendto()");
            return -2;
        }      

        sleep(MCAST_INTERVAL);                     
    }
   
    return 0;
}

2.2 客户端

 linux多播客户端编程思路:

1>建立一个socket;
2>设置多播的参数,例如超时时间TTL,本地回环许可LOOP等
3>加入多播组
4>发送和接收数据
5>从多播组离开
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define MCAST_PORT 8888;
#define MCAST_ADDR "224.0.0.100"     
#define MCAST_INTERVAL 5                        
#define BUFF_SIZE 256                           

int main(int argc, char*argv[])
{  
    struct sockaddr_in local_addr;              /*本地地址*/
    int clientfd = socket(AF_INET, SOCK_DGRAM, 0);     /*建立套接字*/
    if (clientfd == -1)
    {
        perror("socket()");
        return -1;
    }  

    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(MCAST_PORT);

    int err = bind(clientfd,(struct sockaddr*)&local_addr, sizeof(local_addr)) ;
    if(err < 0)
    {
        perror("bind()");
        return -2;
    }

    int loop = 1;
    err = setsockopt(clientfd,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop));
    if(err < 0)
    {
        perror("setsockopt():IP_MULTICAST_LOOP");
        return -3;
    }

    struct ip_mreq mreq;                                    /*加入多播组*/
    mreq.imr_multiaddr.s_addr = inet_addr(MCAST_ADDR); /*多播地址*/
    mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/
    err = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));
    if (err < 0)
    {
        perror("setsockopt():IP_ADD_MEMBERSHIP");
        return -4;
    }

    int times = 0;
    int addr_len = 0;
    char buff[BUFF_SIZE];
    int n = 0;

    for(times = 0;times<5;times++)
    {
        addr_len = sizeof(local_addr);
        memset(buff, 0, BUFF_SIZE);                
        n = recvfrom(clientfd, buff, BUFF_SIZE, 0,(struct sockaddr*)&local_addr,
        &addr_len);
        if( n== -1)
        {
            perror("recvfrom()");
        }

        printf("Recv %dst message from server:%s\n", times, buff);
        sleep(MCAST_INTERVAL);
    }

    err = setsockopt(clientfd, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof
    (mreq));

    close(clientfd);
    return 0;
}