1. 程式人生 > >TCP/IP網路程式設計 學習筆記_15 --多播與廣播

TCP/IP網路程式設計 學習筆記_15 --多播與廣播

轉自:http://blog.csdn.net/u010223072/article/details/48269213

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

多播

  • 多播的資料傳輸特點如下: 
    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地址 
    }

  • 實現多播 
    1,傳送者(Sender)

//
//  main.cpp
//  hello_server
//
//  Created by app05 on 15-9-7.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#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); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

2,接受者(Receiver)

//
//  main.cpp
//  hello_client
//
//  Created by app05 on 15-9-7.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

這裡寫圖片描述

廣播

廣播在功能上和多播是一樣的,都是同時可以向大量客戶傳遞資料。但他們在網路範圍上有區別,多播可以跨越不同的網路,只要加入了多播組就能接收資料。但廣播只能向同一網路中的主機傳輸資料。 
廣播分為:直接廣播與本地廣播,直接廣播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));

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

//
//  main.cpp
//  hello_server
//
//  Created by app05 on 15-9-7.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
//
//  main.cpp
//  hello_client
//
//  Created by app05 on 15-9-7.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

這裡寫圖片描述