您的位置:首页 > 理论基础 > 计算机网络

Linux下的TCP/IP编程----多播和广播的实现

2016-06-05 13:54 573 查看
在前边我们实现的都是服务端客户端这样点对点的通讯,数据只是从一个点到另一个点,但是当我们需要将一份数据同时发给指定的多个人时就遇到了问题,总不能建立多个TCP的长连接或者是多个UDP数据报吧。这时我们就应该考虑使用多播或者时广播来实现我们的需要。

多播:

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数据包时不再是点对点的,而是点对多的发送。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息