天天看點

Linux下的TCP/IP程式設計----多點傳播和廣播的實作【轉載】Linux下的TCP/IP程式設計----多點傳播和廣播的實作

【轉載】 出處:https://blog.csdn.net/wqc_CSDN/article/details/51588769

【轉載】Linux下的TCP/IP程式設計----多點傳播和廣播的實作

2016年06月05日 13:54:10 兜裡有糖心裡不慌 閱讀數:3795 更多

<div class="tags-box space">
							<span class="label">所屬專欄:</span>
															<a class="tag-link" href="https://blog.csdn.net/column/details/linux-tcpip.html" target="_blank" rel="external nofollow"  target="_blank">Linux下的TCP/IP程式設計</a>
															
						</div>
																</div>
			<div class="operating">
													</div>
		</div>
	</div>
</div>
<article>
	<div id="article_content" class="article_content clearfix csdn-tracking-statistics" data-pid="blog" data-mod="popu_307" data-dsm="post">
							<div class="article-copyright">
				版權聲明:本文為部落客原創文章,轉載請注明出處。					https://blog.csdn.net/wqc_CSDN/article/details/51588769				</div>
							            <div class="markdown_views">
						<!-- flowchart 箭頭圖示 勿删 -->
						<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg>
						<p>在前邊我們實作的都是服務端用戶端這樣點對點的通訊,資料隻是從一個點到另一個點,但是當我們需要将一份資料同時發給指定的多個人時就遇到了問題,總不能建立多個TCP的長連接配接或者是多個UDP資料報吧。這時我們就應該考慮使用多點傳播或者時廣播來實作我們的需要。</p>
           

多點傳播:

IP多點傳播(也稱多址廣播或 多點傳播)技術,是一種允許一台或多台主機(多點傳播源)發送單一 資料包到多台主機(一次的,同時的)的TCP/ IP網絡技術。多點傳播作為一點對多點的通信,是節省 網絡帶寬的有效方法之一。多點傳播組是D類IP位址(224.0.0.0~239.255.255.255)。

多點傳播的特點:

1. 多點傳播服務端針對特定多點傳播位址隻發送一次資料

2. 即使隻發送一次資料,但是組内的所有用戶端都能收到資料

3. 多點傳播組數可以在IP位址範圍内任意增加

4. 加入特定的多點傳播組即可接收發往該多點傳播組的資料

多點傳播的實作:

多點傳播其實也是依賴于UDP方式,隻是由之前的UDP點對點發送變成了一對多的發送,而這些改變主要依賴于我們對于socket可選項的設定。可以翻看之前對于socket可選項的介紹。Linux下的TCP/IP程式設計—-socket的可選項

多點傳播Sender的實作:

在實作多點傳播的Sender時我們隻需要将UDP發送資料時的IP位址設定為多點傳播組的IP位址,并為多點傳播資料報設定最大存活時間(TTL)即可。

最大生存時間(TTL):Time To Live的簡稱,是決定資料包傳輸距離的主要因素,其值用正數表示(不能大于十進制的255),并且每經過一個路由器值便減1,當TTL值為0時路由器便會丢棄這個資料包。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define TTL 64
#define BUFF_SIZE 30

void error_handling(char *message);

int main(int argc, char *argv[]){
    //聲明發送者的socket
    int send_socket;
    //聲明多波的位址
    struct sockaddr_in  multicast_addr;
    //資料包的最大生存時間
    int live_time = TTL;
    //檔案指針
    FILE *fp;
    //字元緩沖
    char buff[BUFF_SIZE];
    if(argc != ){
        printf("Uasge : %s <GroupIP> <PORT> \n" ,argv[]);
        exit();
    }

    //初始化socket,設定為多點傳播組的IP位址和端口号
    send_socket = socket(PF_INET,SOCK_DGRAM,);
    memset(&multicast_addr,,sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;
    //和之前的UDP設定方式一樣,隻是輸入的是多點傳播組的IP位址和端口号
    multicast_addr.sin_addr.s_addr = inet_addr(argv[]);
    multicast_addr.sin_port = htons(atoi(argv[]));

    //為多點傳播資料報設定最大生存時間
    setsockopt(send_socket,IPPROTO_IP,IP_MULTICAST_TTL,(void *)&live_time,sizeof(live_time));
    //打開檔案
    if((fp = fopen("test.txt","r")) == NULL){
        error_handling("fopen() error");
    }

    while(!(feof(fp))){
        //從檔案中讀取資料
        fgets(buff,BUFF_SIZE,fp);
        printf("%s",buff);
        //将資料發送到多點傳播組
        sendto(send_socket,buff,BUFF_SIZE,,(struct sockaddr *) &multicast_addr,sizeof(multicast_addr));
        sleep();
    }

    fclose(fp);
    close(send_socket);
return ;
}


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

多點傳播Receiver的實作:

多點傳播的Receiver實作比Sender稍微複雜一點,在Receiver中多了一步聲明加入多點傳播組的步驟。

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

#define TTL 64
#define BUFF_SIZE 30

void error_handling(char *message);

int main(int argc ,char *argv[]){
    //聲明接收者的
    int receiver_socket;
    //每次讀到的字元串長度
    int str_len;
    //用于儲存資料的字元緩沖
    char * buff[BUFF_SIZE];
    //receiver的位址
    struct sockaddr_in receiver_addr;
    //聲明一個用于多點傳播的位址結構體
    struct ip_mreq join_addr;

    if(argc!= ){
        printf("Uasge : %s <GroupIP> <PORT> ",argv[]);
        exit();
    }
    //初始化receiver_socket
    receiver_socket = socket(PF_INET,SOCK_DGRAM,);
    memset(&receiver_addr,,sizeof(receiver_addr));
    receiver_addr.sin_family = AF_INET;
    receiver_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    receiver_addr.sin_port = htons(atoi(argv[]));
    //綁定位址
    if(bind(receiver_socket,(struct sockaddr *)&receiver_addr,sizeof(receiver_addr)) == -){
        error_handling("bind() error");
    }
    //初始化多點傳播位址組
    join_addr.imr_multiaddr.s_addr = inet_addr(argv[]);//要加入的多點傳播組位址
    join_addr.imr_interface.s_addr = htonl(INADDR_ANY);//加入該組的套接字所屬的主機IP位址
    //設定socket中的IP_ADD_MEMBERSHIP選項加入多點傳播組
    setsockopt(receiver_socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void *)&join_addr,sizeof(join_addr));

    while(){
        //接收發送來的消息,因為之前已經将socket注冊到多點傳播中
        str_len = recvfrom(receiver_socket,buff,BUFF_SIZE-,,NULL,);
        if(str_len<){
            break;
        }
        buff[str_len] = ;
        //使用标準的輸入流輸出接受到的資料
        fputs(buff,stdout);
    }
    close(receiver_socket);
return ;
}

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

這樣一個多點傳播的發送者和接收者就編寫完成了,其實在實際使用中并不會很明确的區分發送者和接受者。

廣播:

多點傳播可以很便捷的使我們同時向多個位址發送資料,但前提是要加入對應的多點傳播組,否則就無法接受資料。但是有時我們需要将資料發送給所有的人(例如發通告這一行為),這時多點傳播也無法便捷的實作這個需求(因為要求所有的人都加入多點傳播組),我們就要考慮使用廣播來實作這個功能。

主機之間一對所有的通訊模式,網絡對其中每一台主機發出的信号都進行無條件複制并轉發,所有主機都可以接收到所有資訊(不管你是否需要),但是被限制在二層交換機的區域網路範圍内,禁止廣播資料穿過路由器,防止廣播資料影響大面積的主機。廣播分為直接廣播和本地廣播。

廣播的特點:

1. 可以向廣播域内的所有主機發送資料

2. 不能夠跨越不同的網絡,被路由器所隔離開。

廣播和多點傳播的差別:最主要的差別在于多點傳播可以跨越網絡,不受路由器隔離的影響,隻要加入多點傳播組就可以接收到資料。而廣播隻能是在廣播域内傳輸,被路由器所隔離,防止形成廣播風暴。

廣播的實作:

廣播也是基于UDP實作的,我們同樣需要對socket的可選項進行設定。

廣播Sender的實作:

廣播也是基于UDP的,根據其使用時的IP位址可以分為兩類:

  • 直接廣播:目的廣播域之外的主機向廣播域發送的廣播,IP位址除了網絡位之外,主機位都為1。例如向網絡位址為192.168.10這個網段發送廣播,則IP位址為192.168.10.255
  • 本地廣播:本地廣播使用的IP位址限制為255.255.255.255,是廣播域内的主機向該廣播域發送廣播時使用的IP位址。例如192.168.10.1這個主機想向本網段内所有主機發送廣播,則要發送資料的目的IP位址為192.168.10.255
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define TTL 64
#define BUFF_SIZE 30

void error_handling(char *message);

int main(int argc,char *argv[]){
    //sender的socket
    int sender_socket;
    //廣播位址
    struct sockaddr_in broadcast_addr;
    //檔案指針
    FILE *fp;
    //字元緩沖
    char buff[BUFF_SIZE];
    //用于配置socket參數
    int opt_so_broadcast= ;
    if(argc != ){
        printf("Uasge : %s <GroupIP> <PORT> \n" ,argv[]);
        exit();
    }

    //初始化socket
    sender_socket = socket(PF_INET,SOCK_DGRAM,);
    memset(&broadcast_addr,,sizeof(broadcast_addr));
    broadcast_addr.sin_family = AF_INET;
    broadcast_addr.sin_addr.s_addr = inet_addr(argv[]);
    broadcast_addr.sin_port = htons(atoi(argv[]));

    //設定socket可選項,因為預設生成的會阻止廣播,是以要将可選項中的SO_BROADCAST标志置為1
    setsockopt(sender_socket,SOL_SOCKET,SO_BROADCAST,(void *)&opt_so_broadcast,sizeof(opt_so_broadcast));
    //打開檔案
    if((fp = fopen("word_file.txt","r")) == NULL){
        error_handling("fopen() error");
    }

    while(!(feof(fp))){
        //從檔案中讀取資料
        fgets(buff,BUFF_SIZE,fp);
        printf("%s",buff);
        //将資料發送到多點傳播組
        sendto(sender_socket,buff,strlen(buff),,(struct sockaddr *) &broadcast_addr,sizeof(broadcast_addr));
        sleep();
    }

    fclose(fp);
    close(sender_socket);
return ;
}

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

廣播Receiver的實作:

廣播的Receiver和UDP的并沒有太大的差別,主要是在接受的時候使用的是recvfrom()函數

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

#define TTL 64
#define BUFF_SIZE 30

void error_handling(char * message);

int main(int argc , char *argv[]){
    int receiver_socket;
    struct sockaddr_in receiver_addr;
    int str_len;
    char buff[BUFF_SIZE];

     if(argc!= ){
        printf("Uasge : %s <GroupIP> <PORT> ",argv[]);
        exit();
    }

    //初始化receiver_socket
    receiver_socket = socket(PF_INET,SOCK_DGRAM,);
    memset(&receiver_addr,,sizeof(receiver_addr));
    receiver_addr.sin_family = AF_INET;
    receiver_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    receiver_addr.sin_port = htons(atoi(argv[]));

     //綁定位址
    if(bind(receiver_socket,(struct sockaddr *)&receiver_addr,sizeof(receiver_addr)) == -){
        error_handling("bind() error");
    }

    while(){
        str_len = recvfrom(receiver_socket,buff,BUFF_SIZE-,,NULL,);
        if(str_len < ){
            break;
        }
        buff[BUFF_SIZE] = ;
        fputs(buff,stdout);
    }
    close(receiver_socket);
return ;
}

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

至此socket的多點傳播和廣播就完成了,其實都是在UDP的基礎上進行的。主要對建立的預設的socket進行相應的設定,使得在發送UDP資料包時不再是點對點的,而是點對多的發送。

<script>
					(function(){
						function setArticleH(btnReadmore,posi){
							var winH = $(window).height();
							var articleBox = $("div.article_content");
							var artH = articleBox.height();
							if(artH > winH*posi){
								articleBox.css({
									'height':winH*posi+'px',
									'overflow':'hidden'
								})
								btnReadmore.click(function(){
									articleBox.removeAttr("style");
									$(this).parent().remove();
								})
							}else{
								btnReadmore.parent().remove();
							}
						}
						var btnReadmore = $("#btn-readmore");
						if(btnReadmore.length>0){
							if(currentUserName){
								setArticleH(btnReadmore,3);
							}else{
								setArticleH(btnReadmore,1.2);
							}
						}
					})()
				</script>
				</article>
           

繼續閱讀