1. 程式人生 > >c++ 網絡編程(六)TCP/IP LINUX下 socket編程 多播與廣播 實現一次發送所有組客戶端都能接收到

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

send all users 代碼示例 proto 次數 不可 的人 ssa

原文作者:aircraft

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

一.多播

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

    • 多播的數據傳輸特點如下:
      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地址。
  • 最後說一句啦。本網絡編程入門系列博客是連載學習的,有興趣的可以看我博客其他篇。。。。

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

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

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

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