1. 程式人生 > >linux 組播

linux 組播

1、Linux下組播 IGMP(Internet Group Managerment Protocol)---- Internet組管理協議,是因特網協議家族中的一個組播協議;

2、除了組播(又稱多播),還有單播和廣播;具體的定義和區別網上能百度到;

3、常用的組播地址如下:

224.0.1.0~238.255.255.255為使用者可用的組播地址(臨時組地址),全網範圍內有效。

239.0.0.0~239.255.255.255為本地管理組播地址,僅在特定的本地範圍內有效。

 

4、組播發送程式,server.c: 

/*
*broadcast_server.c - 多播服務程式
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>


#define MCAST_PORT 8888
#define MCAST_ADDR "224.1.1.100"    /*一個區域性連線多播地址,路由器不進行轉發*/
#define MCAST_DATA "Broadcast test data"            
#define MCAST_INTERVAL  2                            /*傳送間隔時間*/

int main(int argc, char*argv)
{
    int s;
    struct sockaddr_in mcast_addr;     
    s = 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);   //IP地址
    mcast_addr.sin_port = htons(MCAST_PORT);        //埠
   
                                                    /*向多播地址傳送資料*/
    while(1) {
        int n = sendto(s,                           /*套接字描述符*/
               MCAST_DATA,     /*資料*/
               sizeof(MCAST_DATA),    /*長度*/
               0,
               (struct sockaddr*)&mcast_addr,
               sizeof(mcast_addr)) ;
        if( n < 0)
        {
            perror("sendto()");
            return -2;
        }      
    printf("send ...\n"); //***debug       
        sleep(MCAST_INTERVAL);                          /*等待一段時間*/
    }
   
    return 0;
}

 

5、組播接收段程式,client.c :

/*
多播客戶端在接收多播組的資料之前需要先加入多播組,當接收完畢後要退出多播組。*/

/*
*broadcast_client.c - 多播的客戶端
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>

#define MCAST_PORT 8888
#define MCAST_ADDR "224.1.1.100"     /*一個區域性連線多播地址,路由器不進行轉發*/
#define MCAST_INTERVAL 2                        /*傳送間隔時間*/
#define BUFF_SIZE 256                           /*接收緩衝區大小*/
int main(int argc, char*argv[])
{  
    int s;                                      /*套接字檔案描述符*/
    struct sockaddr_in local_addr;              /*本地地址*/
    int err = -1;
   
    s = socket(AF_INET, SOCK_DGRAM, 0);     /*建立套接字*/
    if (s == -1)
    {
        perror("socket()");
        return -1;
    }  
   
                                                /*初始化地址*/
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = inet_addr(MCAST_ADDR);  //如果使用預設的htonl(INADDR_ANY);也可以,如果網路中有多個組播組,這裡可以指定
    local_addr.sin_port = htons(MCAST_PORT);   // 組播伺服器的埠
   
                                                /*繫結socket*/
    err = bind(s,(struct sockaddr*)&local_addr, sizeof(local_addr)) ;
    if(err < 0)
    {
        perror("bind()");
        return -2;
    }
   
                                                /*設定迴環許可*/
    int loop = 1;
    err = setsockopt(s,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 = inet_addr("192.168.1.251");   /*這裡可以是預設的介面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;
                                        /*迴圈接收多播組的訊息,100次後退出*/
    for(times = 0;times<100;times++)
    {
        addr_len = sizeof(local_addr);
        memset(buff, 0, BUFF_SIZE);                 /*清空接收緩衝區*/
                                                    /*接收資料*/
        n = recvfrom(s, 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(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));   //退出組播組
       
    close(s);
    return 0;
}
 

編譯:

gcc server.c -o server

gcc client.c -o client

 

6、server端如果沒有配置路由,請新增路由 route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

否則資料無法傳送到組播地址;

 

7、server和client可以在不同網段的PC上執行,如果client端收不到資料,請執行tcpdump看是否有收到組播資料,如果沒有,請檢查網線,如果有,但是client程式卻收不到資料(我的fedora18就出現了這個情況),這時可能是linux 反向過濾造成的;

在/etc/sysctl.conf下
把 net.ipv4.conf.all.rp_filter和 net.ipv4.conf.default.rp_filter設為0即可
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0

reboot重啟即可;

8、client可以執行在多個PC裝置上以接收同一個server端的訊息;

9、但是如果一臺PC上有兩個網絡卡裝置eth0(192.168.0.251)和eth1(192.168.1.251),這兩個裝置不能同時執行client接收同一個組的資料,但是可以接收不同組的資料(比如eth0接收224.1.1.100組的訊息,eth1接收224.1.1.101組的訊息);

 我理解的是一般也不需要這麼用,一個裝置上兩個網路裝置接收一樣的訊息也沒必要;