【轉載】 出處: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>