既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
多播(Multicast)⽅式的数据传输是基于 UDP 完成的。因此 ,与 UDP 服务器端/客⼾端的实现⽅式⾮常接近。区别在于,UDP 数据传输以单⼀⽬标进⾏,而多播数据同时传递到加⼊(注册)特定组的⼤量主机。换⾔之,采⽤多播⽅式时,可以同时向多个主机传递数据。
1.1 多播的数据传输方式以及流量方面的优点
多播的数据传输特点
可整理如下:
- 多播服务器端针对特定多播组,只发送 1 次数据。
- 即使只发送 1 次数据,但该组内的所有客⼾端都会接收数据
- 多播组数可以在 IP 地址范围内任意增加
- 加入特定组即可接收发往该多播组的数据
多播组是 D 类IP地址(224.0.0.0~239.255.255.255),「加⼊多播组」可以理解为通过程序完成如下声明:
在 D 类IP地址中,我希望接收发往⽬标 239.234.218.234 的多播数据
多播是基于UDP完成的,也就是说,多播数据包的格式与UDP数据包相同。只是与一般的UDP数据包不同,向网络传递一个多播数据包时,路由器将复制该数据包并传递到多个主机。像这种,多播需要借助路由器完成。
若通过 TCP 或 UDP 向 1000 个主机发送文件,则共需要传递 1000 次。但是此时如果用多播网络传输文件,则只需要发送一次。这时由 1000 台主机构成的网络中的路由器负责复制文件并传递到主机。就因为这种特性,多播主要用于「多媒体数据实时传输」。
另外,理论上可以完成多播通信,但是不少路由器并不支持多播,或即便支持也因网络拥堵问题故意阻断多播。因此,为了在不支持多播的路由器中完成多播通信,也会使用隧道(Tunneling)技术。
1.2 路由(Routing)和 TTL(Time 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_sock=socket(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_sock=socket(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(time_live);
...
下面是 ip_mreq 结构体的定义:
struct ip_mreq
{
struct in_addr imr_multiaddr; //写入加入组的IP地址
struct in_addr imr_interface; //加入该组的套接字所属主机的IP地址
};
1.3 实现多播 Sender 和 Receiver
多播中用「发送者」(以下称为 Sender) 和「接收者」(以下称为 Receiver)替代服务器端和客户端。顾名思义,此处的 Sender 是多播数据的发送主体,Receiver 是需要多播组加入过程的数据接收主体。下面是示例,示例的运行场景如下:
- Sender : 向 AAA 组广播(Broadcasting)文件中保存的新闻信息
- Receiver : 接收传递到 AAA 组的新闻信息。
Sender .c
#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);
}
recever.c
#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 0
str_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);
}
2 广播
广播(Broadcast)在「一次性向多个主机发送数据」这一点上与多播类似,但传输数据的范围有区别。多播即使在跨越不同网络的情况下,只要加入多播组就能接受数据。相反,广播只能向同一网络中的主机传输数据
。
2.1 广播的理解和实现方法
广播是向同一网络中的所有主机传输数据的方法。与多播相同,广播也是通过 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_sock=socket(PF_INET,SOCK_DGRAM,0);
...
setsockopt(send_sock,SOL_SOCKET,SO_BROADCAST,(void\*)&bcast,sizeof(bcast));
![img](https://img-blog.csdnimg.cn/img_convert/87d8a72585b5c3efa081e317e04eff02.png)
![img](https://img-blog.csdnimg.cn/img_convert/4f54d9ea4c9764a2d2150b1e07865cc7.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
存中...(img-EuHcx2jj-1715801274170)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**