1. 程式人生 > >Linux下的TCP/IP程式設計多播和廣播的實現

Linux下的TCP/IP程式設計多播和廣播的實現

【轉載】

【轉載】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">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 != 3){ printf("Uasge : %s <GroupIP> <PORT> \n" ,argv[0]); exit(1); } //初始化socket,設定為多播組的IP地址和埠號 send_socket = socket(PF_INET,SOCK_DGRAM,0); memset(&multicast_addr,0,sizeof(multicast_addr)); multicast_addr.sin_family = AF_INET; //和之前的UDP設定方式一樣,只是輸入的是多播組的IP地址和埠號 multicast_addr.sin_addr.s_addr = inet_addr(argv[1]); multicast_addr.sin_port = htons(atoi(argv[2])); //為多播資料報設定最大生存時間 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,0,(struct sockaddr *) &multicast_addr,sizeof(multicast_addr)); sleep(2); } fclose(fp); close(send_socket); return 0; } void error_handling(char * message){ fputs(message,stderr); fputc('\n',stderr); exit(1); }

多播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!= 3){
        printf("Uasge : %s <GroupIP> <PORT> ",argv[0]);
        exit(1);
    }
    //初始化receiver_socket
    receiver_socket = socket(PF_INET,SOCK_DGRAM,0);
    memset(&receiver_addr,0,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[2]));
    //繫結地址
    if(bind(receiver_socket,(struct sockaddr *)&receiver_addr,sizeof(receiver_addr)) == -1){
        error_handling("bind() error");
    }
    //初始化多播地址組
    join_addr.imr_multiaddr.s_addr = inet_addr(argv[1]);//要加入的多播組地址
    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(1){
        //接收發送來的訊息,因為之前已經將socket註冊到組播中
        str_len = recvfrom(receiver_socket,buff,BUFF_SIZE-1,0,NULL,0);
        if(str_len<0){
            break;
        }
        buff[str_len] = 0;
        //使用標準的輸入流輸出接受到的資料
        fputs(buff,stdout);
    }
    close(receiver_socket);
return 0;
}

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

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

廣播:

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

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

廣播的特點: 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= 1;
    if(argc != 3){
        printf("Uasge : %s <GroupIP> <PORT> \n" ,argv[0]);
        exit(1);
    }

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

    //設定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),0,(struct sockaddr *) &broadcast_addr,sizeof(broadcast_addr));
        sleep(2);
    }

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

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

廣播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!= 2){
        printf("Uasge : %s <GroupIP> <PORT> ",argv[0]);
        exit(1);
    }

    //初始化receiver_socket
    receiver_socket = socket(PF_INET,SOCK_DGRAM,0);
    memset(&receiver_addr,0,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[1]));

     //繫結地址
    if(bind(receiver_socket,(struct sockaddr *)&receiver_addr,sizeof(receiver_addr)) == -1){
        error_handling("bind() error");
    }

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

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

至此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>