Linux下的TCP/IP编程----多播和广播的实现 【转载】Linux下的TCP/IP编程----多播和广播的实现

【转载】 出处:https://blog.csdn.net/wqc_CSDN/article/details/51588769

【转载】Linux下的TCP/IP编程----多播和广播的实现

多播:

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. 不能够跨越不同的网络,被路由器所隔离开。

广播和多播的区别:最主要的区别在于多播可以跨越网络,不受路由器隔离的影响,只要加入多播组就可以接收到数据。而广播只能是在广播域内传输,被路由器所隔离,防止形成广播风暴。

扫描二维码关注公众号,回复: 3859432 查看本文章

广播的实现:
广播也是基于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>

猜你喜欢

转载自blog.csdn.net/qq_40531442/article/details/83549082