福州做网站制作,wordpress怎么设置关键词,学习教建网站,wordpress建社区第 14 章 多播与广播
14.1 多播 多播#xff08;Multicast#xff09;方式的数据传输是基于 UDP 完成的。因此 #xff0c;与 UDP 服务器端/客户端的实现方式非常接近。区别在于#xff0c;UDP 数据传输以单一目标进行#xff0c;而多播数据同时传递到加入#xff08;注…第 14 章 多播与广播
14.1 多播 多播Multicast方式的数据传输是基于 UDP 完成的。因此 与 UDP 服务器端/客户端的实现方式非常接近。区别在于UDP 数据传输以单一目标进行而多播数据同时传递到加入注册特定组的大量主机。换言之采用多播方式时可以同时向多个主机传递数据。
多播的数据传输方式以及流量方面的优点
多播的数据传输特点可整理如下
多播服务器端针对特定多播组只发送 1 次数据。即使只发送 1 次数据但该组内的所有客户端都会接收数据。多播组数可以在 IP 地址范围内任意增加。加入特定组即可接收发往该多播组的数据。 多播组是 D 类IP地址224.0.0.0~239.255.255.255「加入多播组」可以理解为通过程序完成如下声明 在 D 类IP地址中我希望接收发往目标 239.234.218.234 的多播数据。 多播是基于 UDP 完成的也就是说多播数据包的格式与 UDP 数据包相同。只是与一般的 UDP 数据包不同。向网络传递 1 个多播数据包时路由器将复制该数据包并传递到多个主机。像这样多播需要借助路由器完成。如图所示 若通过 TCP 或 UDP 向 1000 个主机发送文件则共需要传递 1000 次。但是此时如果用多播网络传输文件则只需要发送一次。这时由 1000 台主机构成的网络中的路由器负责复制文件并传递到主机。就因为这种特性多播主要用于「多媒体数据实时传输」。 另外理论上可以完成多播通信但是不少路由器并不支持多播或即便支持也因网络拥堵问题故意阻断多播。因此为了在不支持多播的路由器中完成多播通信也会使用隧道Tunneling技术。
路由Routing和 TTLTime to Live,生存时间以及加入组的办法 为了传递多播数据包必须设置 TTL 。TTL 是 Time to Live的简写是决定「数据包传递距离」的主要因素。TTL 用整数表示并且每经过一个路由器就减一。TTL 变为 0 时该数据包就无法再被传递只能销毁。因此TTL 的值设置过大将影响网络流量。当然设置过小也无法传递到目标。 接下来是 TTL 的设置方法。TTL 是可以通过第九章的套接字可选项完成的。与设置 TTL 相关的协议层为 IPPROTO_IP 选项名为 IP_MULTICAST_TTL。因此可以用如下代码把 TTL 设置为 64
int send_sock;
int time_live 64;
...
send_socksocket(PF_INET,SOCK_DGRAM,0);
setsockopt(send_sock,IPPROTO_IP,IP_MULTICAST_TTL,(void*)time_live,sizeof(time_live);
... 加入多播组也通过设置套接字可选项来完成。加入多播组相关的协议层为 IPPROTO_IP选项名为 IP_ADD_MEMBERSHIP 。可通过如下代码加入多播组
int recv_sock;
struct ip_mreq join_adr;
...
recv_socksocket(PF_INET,SOCK_DGRAM,0);
...
join_adr.imr_multiaddr.s_addr多播组地址信息;
join_adr.imr_interface.s_addr加入多播组的主机地址信息;
setsockopt(recv_sock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(void*)join_adr,sizeof(join_adr);
... 下面是 ip_mreq 结构体的定义
struct ip_mreq
{struct in_addr imr_multiaddr; //写入加入组的IP地址struct in_addr imr_interface; //加入该组的套接字所属主机的IP地址
};
实现多播 Sender 和 Receiver 多播中用「发送者」以下称为 Sender 和「接收者」以下称为 Receiver替代服务器端和客户端。顾名思义此处的 Sender 是多播数据的发送主体Receiver 是需要多播组加入过程的数据接收主体。下面是示例示例的运行场景如下
Sender : 向 AAA 组广播Broadcasting文件中保存的新闻信息Receiver : 接收传递到 AAA 组的新闻信息。
下面是示例代码
news_sender
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h#define TTL 64
#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int send_sock;struct sockaddr_in mul_adr;int time_live TTL;FILE *fp;char buf[BUF_SIZE];if (argc ! 3){printf(Usage : %s GroupIP PORT\n, argv[0]);exit(1);}send_sock socket(PF_INET, SOCK_DGRAM, 0); //创建 UDP 套接字memset(mul_adr, 0, sizeof(mul_adr));mul_adr.sin_family AF_INET;mul_adr.sin_addr.s_addr inet_addr(argv[1]); //必须将IP地址设置为多播地址mul_adr.sin_port htons(atoi(argv[2]));//指定套接字中 TTL 的信息setsockopt(send_sock, IPPROTO_IP, IP_MULTICAST_TTL, (void *)time_live, sizeof(time_live));if ((fp fopen(news.txt, r)) NULL)error_handling(fopen() error);while (!feof(fp)) //如果文件没结束就返回0{fgets(buf, BUF_SIZE, fp);sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)mul_adr, sizeof(mul_adr));sleep(2);}fclose(fp);close(send_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}
news_receiver
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int recv_sock;int str_len;char buf[BUF_SIZE];struct sockaddr_in adr;struct ip_mreq join_adr;if (argc ! 3){printf(Usage : %s GroupIP PORT\n, argv[0]);exit(1);}recv_sock socket(PF_INET, SOCK_DGRAM, 0);memset(adr, 0, sizeof(adr));adr.sin_family AF_INET;adr.sin_addr.s_addr htonl(INADDR_ANY);adr.sin_port htons(atoi(argv[2]));if (bind(recv_sock, (struct sockaddr *)adr, sizeof(adr)) -1)error_handling(bind() error);//初始化结构体join_adr.imr_multiaddr.s_addr inet_addr(argv[1]); //多播组地址join_adr.imr_interface.s_addr htonl(INADDR_ANY); //待加入的IP地址//利用套接字选项 IP_ADD_MEMBERSHIP 加入多播组完成了接受指定的多播组数据的所有准备setsockopt(recv_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)join_adr, sizeof(join_adr));while (1){//通过 recvfrom 函数接受多播数据。如果不需要知道传输数据的主机地址信息可以向recvfrom函数的第5 6参数分别传入 NULL 0str_len recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);if (str_len 0)break;buf[str_len] 0;fputs(buf, stdout);}close(recv_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}
运行结果 通过结果可以看出使用 sender 多播信息通过 receiver 接收广播如果延迟运行 receiver 将无法接受之前发送的信息。
14.2 广播 广播Broadcast在「一次性向多个主机发送数据」这一点上与多播类似但传输数据的范围有区别。多播即使在跨越不同网络的情况下只要加入多播组就能接受数据。相反广播只能向同一网络中的主机传输数据。
广播的理解和实现方法 广播是向同一网络中的所有主机传输数据的方法。与多播相同广播也是通过 UDP 来完成的。根据传输数据时使用的IP地址形式广播分为以下两种
直接广播Directed Broadcast本地广播Local Broadcast 二者在实现上的差别主要在于IP地址。直接广播的IP地址中除了网络地址外其余主机地址全部设置成 1。例如希望向网络地址 192.12.34 中的所有主机传输数据时可以向 192.12.34.255 传输。换言之可以采取直接广播的方式向特定区域内所有主机传输数据。 反之本地广播中使用的IP地址限定为 255.255.255.255 。例如192.32.24 网络中的主机向 255.255.255.255 传输数据时数据将传输到 192.32.24 网络中所有主机。 数据通信中使用的IP地址是与 UDP 示例的唯一区别。默认生成的套接字会阻止广播因此只需通过如下代码更改默认设置
int send_sock;
int bcast;
...
send_socksocket(PF_INET,SOCK_DGRAM,0);
...
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void*)bcast,sizeof(bcast));
...
实现广播数据的 Sender 和 Receiver 下面是广播数据的 Sender 代码
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int send_sock;struct sockaddr_in broad_adr;FILE *fp;char buf[BUF_SIZE];int so_brd 1;if (argc ! 3){printf(Usage : %s GroupIP PORT\n, argv[0]);exit(1);}send_sock socket(PF_INET, SOCK_DGRAM, 0); //创建 UDP 套接字memset(broad_adr, 0, sizeof(broad_adr));broad_adr.sin_family AF_INET;broad_adr.sin_addr.s_addr inet_addr(argv[1]);broad_adr.sin_port htons(atoi(argv[2]));setsockopt(send_sock, SOL_SOCKET, SO_BROADCAST, (void *)so_brd, sizeof(so_brd));if ((fp fopen(news.txt, r)) NULL)error_handling(fopen() error);while (!feof(fp)) //如果文件没结束就返回0{fgets(buf, BUF_SIZE, fp);sendto(send_sock, buf, strlen(buf), 0, (struct sockaddr *)broad_adr, sizeof(broad_adr));sleep(2);}fclose(fp);close(send_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}
ps
sendto() 函数用于在无连接的数据报套接字如UDP套接字中发送数据它不会进行连接的建立和断开操作因此每次发送数据时都需要指定目标地址。这使得UDP套接字适用于一对多或多对多的通信场景例如广播和组播。 while (!feof(fp)) 循环读取文件直到文件结束。feof() 函数用于检查是否已到达文件末尾。
下面是Receiver代码
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h#define BUF_SIZE 30
void error_handling(char *message);int main(int argc, char *argv[])
{int recv_sock; // 接收数据的套接字int str_len; // 接收到的数据长度char buf[BUF_SIZE]; // 存放接收到的数据的缓冲区struct sockaddr_in adr; // 地址结构体用于存储服务器绑定的地址信息// 检查命令行参数确保指定了端口号if (argc ! 2){printf(Usage : %s PORT\n, argv[0]);exit(1);}// 创建一个UDP套接字recv_sock socket(PF_INET, SOCK_DGRAM, 0);// 初始化地址结构体绑定服务器的IP地址和端口号memset(adr, 0, sizeof(adr));adr.sin_family AF_INET; // 使用IPv4地址族adr.sin_addr.s_addr htonl(INADDR_ANY); // INADDR_ANY表示接受任意IP地址发送的数据adr.sin_port htons(atoi(argv[1])); // 从命令行参数获取端口号并转换为网络字节序// 绑定套接字和地址信息if (bind(recv_sock, (struct sockaddr *)adr, sizeof(adr)) -1)error_handling(bind() error);while (1){// 通过recvfrom函数接收数据。如果不需要知道传输数据的主机地址信息可以向recvfrom函数的第5、6参数传入NULL和0str_len recvfrom(recv_sock, buf, BUF_SIZE - 1, 0, NULL, 0);if (str_len 0)break;buf[str_len] 0; // 添加字符串结束符确保打印输出时不会超出接收到的数据fputs(buf, stdout); // 将接收到的数据打印到标准输出}// 关闭套接字close(recv_sock);return 0;
}// 错误处理函数用于输出错误信息并退出程序
void error_handling(char *message)
{fputs(message, stderr);fputc(\n, stderr);exit(1);
}运行结果 习题
1、TTL 的含义是什么请从路由器的角度说明较大的 TTL 值与较小的 TTL 值之间的区别及问题。 TTL 是决定「数据包传递距离」的主要因素。TTL 每经过一个路由器就减一。TTL 变为 0 时数据包就无法再被传递只能销毁。因此TTL设置过大会影响网络流量。当然设置过小无法传递到目标。
2、多播与广播的异同点是什么请从数据通信的角度进行说明。 在「一次性向多个主机发送数据」这一点上与多播类似但传输的数据范围有区别。多播即使在跨越不同网络的情况下只要加入多播组就能接受数据。相反广播只能向同一网络中的主机传输数据。
3、多播也对网络流量有利请比较 TCP 交换方式解释其原因。 TCP 是必须建立一对一的连接如果要向1000个主机发送文件就得传递1000次。但是此时用多播方式传输数据就只需要发送一次。