c++ 網絡編程(六)LINUX下 socket編程 多播與廣播 實現一次發送所有組客戶端都能接收到


 

原文作者:aircraft

原文鏈接:https://www.cnblogs.com/DOMLX/p/9614288.html

 

 

 

本網絡編程入門系列博客是連載學習的,有興趣的可以看我博客其他篇。。。。c++ 網絡編程課設入門超詳細教程 ---目錄

 

一.多播

鍥子:有這么一種情況,網絡電台可能需要同時向成千上萬的用戶傳輸相同的數據,如果用我們以前講過的傳輸形式,每個用戶都傳輸一次,這樣肯定是不合理的。因此,就引入了多播技術來解決這個問題,它可以同時向大量用戶發送相同數據。其基本原理是這樣的:有個多播組,只要加入這個組里的所有客服端,服務端發送的數據它們都能收到,具體傳輸到多播組里的每個客戶是由路由完成的(如果路由器不支持多播或網絡堵塞,實現多播也會使用隧道技術)

 

    • 多播的數據傳輸特點如下:
      1,多播服務器端針對特定多播組,只需發送1次數據,該組內的所有所有客服端都能接收數據。
      2,多播組數可在IP地址范圍內任意增加。

    • 設置生存時間和加入多播組的方法
      1,設置生存時間:只指服務端發送的數據包最遠能傳遞的距離,用整數表示,並且每經過1個路由器就減1,當為0時,該數據包無法再被傳遞,只能銷毀。因此,這個值設置過大將影響網絡流量。當然,設置過小也會無法傳遞到目標(通過套接字可選項設置,示例代碼中有使用方法)。

      2,加入多播組:也是通過套接字可選項設置,示例代碼中有使用方法,這里只介紹多播組的結構體ip_mreq。

    •  

      struct ip_mreq
      {
      struct in_addr imr_multiaddr; //多播組的IP地址
      struct in_addr imr_interface; //加入的客服端主機IP地址

       

      }

       

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

#define TTL 64    //數據包生存時間,即最多可以傳遞經過第64個路由時銷毀
#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, const char * argv[]) {
    int send_sock;
    struct sockaddr_in mul_adr;
    int time_live = TTL;
    FILE *fp;
    char buf[BUF_SIZE];
    if (argc != 3) {
        printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
        exit(1);
    }

    //基於UDP的多播
    send_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&mul_adr, 0, sizeof(mul_adr));
    mul_adr.sin_family = AF_INET;
    mul_adr.sin_addr.s_addr = inet_addr(argv[1]);
    mul_adr.sin_port = htons(atoi(argv[2]));

    //設置生存時間(除了這里其它基本和UDP編寫一樣)
    setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&time_live, sizeof(time_live));

    if((fp = fopen("/Users/app05/Desktop/test.txt", "r")) == NULL)
        error_handling("fopen() error");

    while (!feof(fp)) //如果文件結束,則返回非0值,否則返回0
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&mul_adr, sizeof(mul_adr));
        sleep(1); //只是為了加個傳輸數據時間間隔,沒有特殊意義
    }

    fclose(fp);
    close(send_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

 

接收者(receiver):

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, const char * argv[]) {
    int recv_sock;
    int str_len;
    char buf[BUF_SIZE];
    struct sockaddr_in adr;
    struct ip_mreq join_adr; //多播組結構體

    if(argc != 3)
    {
        printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
        exit(1);
    }

    recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_addr.s_addr = htonl(INADDR_ANY);
    adr.sin_port = htons(atoi(argv[2]));

    if(bind(recv_sock, (struct sockaddr *)&adr, sizeof(adr)) == -1)
        error_handling("bind() error");

    //加入多播組
    join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]);
    join_adr.imr_interface.s_addr = htonl(INADDR_ANY);
    setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr));

    while (1) {
        str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);//只需要多播組IP地址,不關心自己主機地址
        if(str_len < 0)
            break;
        buf[str_len] = 0;
        fputs(buf, stdout);
    }

    close(recv_sock);
    return 0;
}


void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

 

二.廣播

廣播在功能上和多播是一樣的,都是同時可以向大量客戶傳遞數據。但他們在網絡范圍上有區別,多播可以跨越不同的網絡,只要加入了多播組就能接收數據。但廣播只能向同一網絡中的主機傳輸數據。
廣播分為:直接廣播與本地廣播,直接廣播sender的IP地址只需指定網絡地址,主機地址全部填255。這樣處在這個網絡地址里的所有主機就可以接收數據了。而本地廣播sender的IP地址寫255.255.255.255,這樣本地網絡所有主機就可以接收數據了。

//將SO_BROADCAST可選項設置為1就表示開啟了套接字廣播功能,默認是關閉的。
int bcast = 1;
setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void *) &bcast, sizeof(bcast));

 

 

下面就多播的代碼示例稍作修改,本地廣播的示例如下:

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

#define TTL 64    //數據包生存時間,即最多可以傳遞經過第64個路由時銷毀
#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, const char * argv[]) {
    int send_sock;
    struct sockaddr_in mul_adr;
    int time_live = TTL;
    FILE *fp;
    char buf[BUF_SIZE];
    if (argc != 3) {
        printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
        exit(1);
    }

    //基於UDP的多播
    send_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&mul_adr, 0, sizeof(mul_adr));
    mul_adr.sin_family = AF_INET;
    mul_adr.sin_addr.s_addr = inet_addr(argv[1]);
    mul_adr.sin_port = htons(atoi(argv[2]));

    //設置生存時間(除了這里其它基本和UDP編寫一樣)
    //setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&time_live, sizeof(time_live));

    /*add:廣播修改處*/
    //默認套接字是關閉廣播的,開啟如下:
    int so_brd = 1;  //設置為1就可以開啟廣播
    setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void *)&so_brd, sizeof(so_brd));

    if((fp = fopen("/Users/app05/Desktop/test.txt", "r")) == NULL)
        error_handling("fopen() error");

    while (!feof(fp)) //如果文件結束,則返回非0值,否則返回0
    {
        fgets(buf, BUF_SIZE, fp);
        sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)&mul_adr, sizeof(mul_adr));
        sleep(1); //只是為了加個傳輸數據時間間隔,沒有特殊意義
    }

    fclose(fp);
    close(send_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

另一個修改:

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, const char * argv[]) {
    int recv_sock;
    int str_len;
    char buf[BUF_SIZE];
    struct sockaddr_in adr;
    //struct ip_mreq join_adr; //多播組結構體

    if(argc != 2)
    {
        printf("Usage : %s <GroupIp> <Port> \n", argv[0]);
        exit(1);
    }

    recv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    memset(&adr, 0, sizeof(adr));
    adr.sin_family = AF_INET;
    adr.sin_addr.s_addr = htonl(INADDR_ANY);
    adr.sin_port = htons(atoi(argv[1]));

    if(bind(recv_sock, (struct sockaddr *)&adr, sizeof(adr)) == -1)
        error_handling("bind() error");

    //加入多播組
    //join_adr.imr_multiaddr.s_addr = inet_addr(argv[1]);
    //join_adr.imr_interface.s_addr = htonl(INADDR_ANY);
    //setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&join_adr, sizeof(join_adr));

    while (1) {
        str_len = recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);//只需要多播組IP地址,不關心自己主機地址
        if(str_len < 0)
            break;
        buf[str_len] = 0;
        fputs(buf, stdout);
    }

    close(recv_sock);
    return 0;
}


void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

 

 

三.多播與廣播的區別

  • 多播:“多播”也可以稱為“組播”,在網絡技術的應用並不是很多,網上視頻會議、網上視頻點播特別適合采用多播方式。因為如果采用單播方式,逐個節點傳輸,有多少個目標節點,就會有多少次傳送過程,這種方式顯然效率極低,是不可取的;如果采用不區分目標、全部發送的廣播方式,雖然一次可以傳送完數據,但是顯然達不到區分特定數據接收對象的目的。采用多播方式,既可以實現一次傳送所有目標節點的數據,也可以達到只對特定對象傳送數據的目的。   IP網絡的多播一般通過多播IP地址來實現。多播IP地址就是D類IP地址,即224.0.0.0至239.255.255.255之間的IP地址。Windows 2000中的DHCP管理器支持多播IP地址的自動分配。

 

  • 廣播:“廣播”在網絡中的應用較多,如客戶機通過DHCP自動獲得IP地址的過程就是通過廣播來實現的。但是同單播和多播相比,廣播幾乎占用了子網內網絡的所有帶寬。拿開會打一個比方吧,在會場上只能有一個人發言,想象一下如果所有的人同時都用麥克風發言,那會場上就會亂成一鍋粥。集線器由於其工作原理決定了不可能過濾廣播風暴,一般的交換機也沒有這一功能,不過現在有的網絡交換機(如全向的QS系列交換機)也有過濾廣播風暴功能了,路由器本身就有隔離廣播風暴的作用。   廣播風暴不能完全杜絕,但是只能在同一子網內傳播,就好像喇叭的聲音只能在同一會場內傳播一樣,因此在由幾百台甚至上千台電腦構成的大中型局域網中,一般進行子網划分,就像將一個大廳用牆壁隔離成許多小廳一樣,以達到隔離廣播風暴的目的。   在IP網絡中,廣播地址用IP地址“255.255.255.255”來表示,這個IP地址代表同一子網內所有的IP地址。
     
     
     
  •  最后說一句啦。本網絡編程入門系列博客是連載學習的,有興趣的可以看我博客其他篇。。。。c++ 網絡編程課設入門超詳細教程 ---目錄

     

    好了今天對網絡編程的學習就到這里結束了,小飛機我要撤了去吃飯了。,,,很多人大學都很迷茫不知道學點什么好,,,,,管他的,想那么多干嘛,先學了再說,對技術如有偏見,那么你的領域就局限於此了---《一專多精》

     

     

     

     

    參考博客:https://blog.csdn.net/u010223072/article/details/48269213

    參考書籍:《TCP/IP 網絡編程 --尹聖雨》

  • 若有興趣交流分享技術,可關注本人公眾號,里面會不定期的分享各種編程教程,和共享源碼,諸如研究分享關於c/c++,python,前端,后端,opencv,halcon,opengl,機器學習深度學習之類有關於基礎編程,圖像處理和機器視覺開發的知識

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM