单播、广播、组播和任播
文章目录
- 一、单播
- 二、广播
- 三、组播
- 四、任播
- 代码示例:
- 五、各种播的比较
一、单播
- 单播(Unicast)是一种网络通信方式,它指的是在网络中从一个源节点到一个单一目标节点对的传输模式。单播传输时,数据包从发送端直接发送到指定接收端,而不是广播给网络中的所有节点或组播给多个指定节点。单播是最常见的网络通信方式,主要应用于日常的互联网通信,如访问网页、发送电子邮件、文件下载等。
- 单播的特点
举个例子
- 当用户访问一个网页时,用户的设备会通过单播请求服务器的网页内容,服务器再单播传输响应给用户设备。
- 发送电子邮件也是一个单播过程:邮件从发送者的邮件服务器传输到接收者的邮件服务器。
二、广播
- 什么是广播?
广播(Broadcast)这种网络通信方式,用于将数据从一个节点传输到同一网络中的所有其他节点。广播数据包会被发送到一个特殊的广播地址,网络中的所有设备都会接收到这个数据包,不论设备是否需要该信息。这种通信方式一般用于局域网(LAN)中的信息共享。
广播这个东西,并不是所有的网络都支持,通常广播只是在局域网中,IPv6也不只是广播。在以太网中使用全1的地址,来表示广播地址。例如255.255.255.255 。在广播中,发送端并不指定特定的接收方,而是将数据包发送到该网络中的所有设备。由于广播会广泛传播,在网络中光标的数据通常是诸如网络探测和广告等,这种方式也常被黑客用来进行入侵和攻击。
-
广播的特点
-
广播的类型
在计算机网络中,广播有两种主要类型
- 有限广播(Limited Broadcast):
使用特殊的IP地址255.255.255.255
仅在发送数据包的子网中传播,不能跨越路由器
例如:DHCP请求使用有限广播,因为客户端不知道服务器的IP地址 - 定向广播(Directed Broadcast)
使用的是子网广播地址,例如在192.168.1.0/24子网中的定向广播地址是192.。168.1.255 。
定向广播包只会被发送到特定的子网,因此可以跨越一些允许定向广播的路由器
通常用于特定子网中发送广播信息,二人不是整个局域网 - 假如有一个网络192.168.1.0/224,它的广播地址是192.168.1.255,那么向1192.168.1.255发送的数据包会被该子网中的所有设备接收
如果有设备A向192.168.1.55发送广播包,那么192.168.1.0/24子网内的所有设备都会接收到该数据包
使用ping255.255.255.255命令(有限广播),可以测试网络中是否有设备响应,常用于网络调试。
-
广播的应用场景
-
广播在不同协议中的使用
- IP v4协议:IPv4中支持广播,有专门的广播地址
- IPv6协议:IPv6取消了广播功能,代之以组播(Multicast)和任播(Anycast)通信方式,组播比广播高效且对资源利用更优化。
- 代码示例
下面是一个用C++实现UDP广播的示例代码,这个程序将创建一个UDP套接字,并向局域网内的广播地址发送消息。代码将消息广播到默认的局域网广播地址(如255.255.255.255或某个特定的子网广播地址,比如192.168.1.255)
注:运行广播代码时,请确保防火墙允许广播包的发送,否则可能会被拦截。另外,这段代码需要在支持BSD套接字的环境中允许,比如Linux或Windows。
//Linux版本
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
#define BROADCAST_PORT 8888 //发送广播的端口
#define BROADCAST_IP "255.255.255.255" //广播地址
int main() {
//创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return -1;
}
//启用广播选项
int BroadcastEnable = 1;//启用广播
if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &BroadcastEnable, sizeof(BroadcastEnable)))
{
close(sockfd);
perror("setsockopt");
return -1;
}
//配置广播地址
struct sockaddr_in broadcastAddr; //配置广播地址
memset(&broadcastAddr, 0, sizeof(broadcastAddr));
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_addr.s_addr = inet_addr(BROADCAST_IP);
broadcastAddr.sin_port = htons(BROADCAST_PORT);
//广播的消息
const char* message="HEllo,this is a broadcast message!";
//发送广播消息
int sendResult=sendto(sockfd, message, strlen(message),0,(struct sockaddr*)&broadcastAddr,sizeof(broadcastAddr));
if(sendResult<0)
{
perror("send Faliure!");
}
else{
cout<<"send success!"<<"and sent:"<<message<<endl;
}
//关闭套接字
close(sockfd);
return 0;
}
//Windows版本
#include <iostream>
#include <cstring>
#include <winsock2.h> //Windows套接字库
#include <Ws2tcpip.h> //Windows套接字IPv4地址库
using namespace std;
#pragma comment(lib, "Ws2_32.lib") //自动连接ws2_32.lib库
#define BROADCAST_PORT 8888 //发送广播的端口
#define BROADCAST_IP "255.255.255.255" //广播地址
int main() {
//初始化Winsock库
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
cerr << "WSAStartup failed." <<WSAGetLastError()<< endl;
return -1;
}
//创建UDP套接字
SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return -1;
}
//启用广播选项
int BroadcastEnable = 1;//启用广播
if(setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,(char *)&BroadcastEnable,sizeof(BroadcastEnable)))
{
cerr<<"setsockopt failed."<<WSAGetLastError()<<endl;
closesocket(sockfd);
WSACleanup();
return -1;
}
//配置广播地址
struct sockaddr_in broadcastAddr; //配置广播地址
memset(&broadcastAddr, 0, sizeof(broadcastAddr));
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_addr.s_addr = inet_addr(BROADCAST_IP);
broadcastAddr.sin_port = htons(BROADCAST_PORT);
//广播的消息
const char* message="HEllo,this is a broadcast message!";
//发送广播消息
int sendResult=sendto(sockfd, message, strlen(message),0,(struct sockaddr*)&broadcastAddr,sizeof(broadcastAddr));
if(sendResult<0)
{
perror("send Faliure!");
}
else{
cout<<"send success!"<<"and sent:"<<message<<endl;
}
//关闭套接字
closesocket(sockfd);
return 0;
}
三、组播
- 什么是组播多播?
组播和多播实际上是同一个概念,两者只是翻译上的差异。组播(或多播)也是一种网络通信方法,允许数据只发送一次,但可以由多台主机接收。其主要应用于需要多个接收方实时同步数据的场景,比如在线视频直播、网络游戏、股票行情等
组播通常用于局域网,因为许多路由器和网络设备(特别是家用和小型网络设备)不支持跨互联阿的组播路由。此外,组播的管理和传输在局域网中更为高效和可控。。不过,大型企业或互联网服务提供商可能在其内部网络中使用组播、以实现高效的数据分发。
- 组播的原理
- 具体来说,在一个局域网中的组播过程如下:
1.假设在局域网内有5台主机:A、B、C、D、E
2.通过分配一个组播地址和端口(比如239.255.0.1:8888)来定义组
3.现在,如果主机A、B和C加入了这个组播组(即订阅了233.255.0.。1:8888的组播消息),那么D和E不会收到该组播消息,因为它们没有加入该组.
4.当A发送一条组播消息到239.255.0.1:8888时,B和C会接收这条消息,而D和E不会收到,因为组播只会将数据发送给加入了该组播地址的主机
5.组播的IP和端口是程序自己随意选择的(避开常用端口,要大于1024),只要在239.0.0.0到239.。255.。255.255这个范围内就可以。
- 代码实现(基于Windows版本)
发送端
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
int main()
{
//初始化WinSock
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return 1;
}
//创建UDP套接字
SOCKET sendsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sendsock==INVALID_SOCKET)
{
cerr<<"socket error"<<endl;
WSACleanup();
return -1;
}
//设置组播地址和端口
sockaddr_in multicastAddr;
multicastAddr.sin_family = AF_INET;
multicastAddr.sin_addr.s_addr = inet_addr("239.255.0.1");
multicastAddr.sin_port = htons(8888);
//设置套接字为可广播,以确保数据可以通过广播发送到组播地址
int ttl = 1;
setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
//发送组播消息
const char* message = "Hello, multicast!";
int sendResult = sendto(sendsock, message, strlen(message), 0, (sockaddr*)&multicastAddr, sizeof(multicastAddr));
if(sendResult==SOCKET_ERROR)
{
cerr<<"sendto error"<<endl;
closesocket(sendsock);
WSACleanup();
return -1;
}
cout<<"sendto success and message is:"<<message<<endl;
//关闭套接字
closesocket(sendsock);
WSACleanup();
}
接收端
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
int main()
{
//初始化WinSock
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
return 1;
}
//创建UDP套接字
SOCKET recvsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(recvsock==INVALID_SOCKET)
{
cerr<<"socket error"<<endl;
WSACleanup();
return -1;
}
//绑定组播地址和端口
sockaddr_in recvAddr;
recvAddr.sin_family = AF_INET;
recvAddr.sin_addr.s_addr = INADDR_ANY;
recvAddr.sin_port = htons(8888);
if(bind(recvsock,(sockaddr*)&recvAddr,sizeof(recvAddr))==SOCKET_ERROR)
{
cerr<<"bind error"<<endl;
closesocket(recvsock);
WSACleanup();
return -1;
}
//加入组播组
ip_mreq multicastRequest;
multicastRequest.imr_multiaddr.s_addr = inet_addr("239.255.0.1");//组播地址
multicastRequest.imr_interface.s_addr = INADDR_ANY; //本地任意接口
if(setsockopt(recvsock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&multicastRequest,sizeof(multicastRequest)))
{
cerr<<"setsockopt error"<<endl;
closesocket(recvsock);
WSACleanup();
return -1;
}
//接收组播消息
char recvBuf[1024];
sockaddr_in senderAddr;
int senderAddrLen = sizeof(senderAddr);
int recvlen= recvfrom(recvsock,recvBuf,sizeof(recvBuf),0,(sockaddr*)&senderAddr,&senderAddrLen);
if(recvlen>0)
{
recvBuf[recvlen] = '\0';
cout<<"receive message:"<<recvBuf<<endl;
}
else{
cerr<<"recvfrom error"<<endl;
}
//退出组播组
setsockopt(recvsock,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char *)&multicastRequest,sizeof(multicastRequest));
//关闭套接字
closesocket(recvsock);
WSACleanup();
return 0;;
}
- 注意事项
1.组播地址选择,在局域网中,239.0.0.0到239.255.255.255是专用组播地址,通常用于本地组播
2.TTL设置:通过设置TTL(Time-To_Live)为1,可确保组播消息不会被路由器转发到其他网段
int ttl = 1;
setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
IPPORTTO_IP:这个参数指定了将要设置对的选项所需的协议级别,IPPORTTO_IP表示这是IPv4协议级别的选项。对于IPv6,相应的常量是IPPORTTO_IPV6 。
IP_MULTICAST_TTL:这是要设置的选项名,IP_MULTICAST_TTL用于指定多播数据包的生存时间(TTL),TTL定义了数据包在网络中可以经过的最大路由器数(或跳数)。每当数据包经过一个路由器时,其TTL值减1.当TTL值减到0时,数据包被丢弃。
if(setsockopt(recvsock,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char *)&multicastRequest,sizeof(multicastRequest)))
{
cerr<<"setsockopt error"<<endl;
closesocket(recvsock);
WSACleanup();
return -1;
}
- sock:套接字描述符
- IPPORTTO_IP:指定选项所在的协议层。
- IP_ADD_MEMBERSHIP:这是要设置选项的名称。它告诉系统,我们想要将一个套接字加入到一个多播组中
- &multicast_request:指向一个ip_mreq结构的指针,该结构包含了多播组的地址以及本地接口的地址(或者是一个特殊的值来表示所有接口)。这个结构定义了套接字将要加入的多播组。
- ip_mreq结构通常定义如下(在<netinet/in.h>或类似头文件中):
struct ip_mreq{
struct in_addr imr_umltiaddr;//多播组的IP地址
struct in_addrr imr_interface;//本地接口的IP地址,或INADDR_ANY表示所有接口
}
- sizeof(multicast_request):指定了multicast_request结构的大小,以字节为单位。
- 代码实现(基于Linux)
//服务端
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
using namespace std;
#define PORT 8080
#define MAX_SIZE 1024
int main()
{
//创建UDP套接字
int sendsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sendsock<0)
{
cerr<<"socket error"<<endl;
return -1;
}
//设置组播地址和端口
sockaddr_in multicastAddr;
multicastAddr.sin_family = AF_INET;
multicastAddr.sin_addr.s_addr = inet_addr("239.255.0.1");
multicastAddr.sin_port = htons(8888);
//设置套接字为可广播,以确保数据可以通过广播发送到组播地址
int ttl = 1;
setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_TTL, (char*)&ttl, sizeof(ttl));
//发送组播消息
const char* message = "Hello, multicast!";
int sendResult = sendto(sendsock, message, strlen(message), 0, (sockaddr*)&multicastAddr, sizeof(multicastAddr));
if(sendResult<0)
{
cerr<<"sendto error"<<endl;
close(sendsock);
return -1;
}
cout<<"sendto success and message is:"<<message<<endl;
//关闭套接字
close(sendsock);
return 0;
}
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
using namespace std;
#define PORT 8080
#define MAX_SIZE 1024
int main()
{
//创建UDP套接字
int recvsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(recvsock<0)
{
cerr<<"socket error"<<endl;
return -1;
}
//绑定组播地址和端口
sockaddr_in recvAddr;
recvAddr.sin_family = AF_INET;
recvAddr.sin_addr.s_addr = INADDR_ANY;
recvAddr.sin_port = htons(8888);
if(bind(recvsock,(sockaddr*)&recvAddr,sizeof(recvAddr))<0)
{
cerr<<"bind error"<<endl;
close(recvsock);
return -1;
}
//加入组播组
ip_mreq multicastRequest;
multicastRequest.imr_multiaddr.s_addr = inet_addr("239.255.0.1");//组播地址
multicastRequest.imr_interface.s_addr = INADDR_ANY; //本地任意接口
//接收组播消息
char recvBuf[1024];
sockaddr_in senderAddr;
socklen_t senderAddrLen = sizeof(senderAddr);
int recvlen= recvfrom(recvsock,recvBuf,MAX_SIZE,0,(sockaddr*)&senderAddr,&senderAddrLen);
if(recvlen>0)
{
recvBuf[recvlen] = '\0';
cout<<"receive message:"<<recvBuf<<endl;
}
else{
cerr<<"recvfrom error"<<endl;
}
//退出组播组
setsockopt(recvsock,IPPROTO_IP,IP_DROP_MEMBERSHIP,(char *)&multicastRequest,sizeof(multicastRequest));
//关闭套接字
close(recvsock);
}
四、任播
- 什么是任播?
任播(Anycast)是指多个服务器或节点共享一个IP地址,通过网络协议中的路由机制,用户的请求自动选择距离最近或最优的服务器进行响应。与传统的单播(Unicast)和组播(Multicast)不同,任播的核心在于“发送到最近的一个”
任播与组播的区别:在组播中,消息会被发送到一组所有订阅该IP地址的节点。但在任播中,消息只会到达同一IP地址下的一个最优节点。
任播可以使用任何可路由的IP地址,通常是常规的单播地址(A类、B类或C类IP地址),而不是D类地址。
任播实现的关键在于多个主机共享同一单播IP地址,通过路由协议(例如BGP或OSPF)来决定最近或最佳路径,而不是通过适用特殊的IP地址类被来实现。
-
任播的特点
-
任播的原理
- 任播最优路由决策的过程
任播的最优路由通常由BGP(边界网关协议)或OSPF(开放式最短路径优先协议)等网络路由协议来实现。以下是最优路由的决策过程
-
地理距离和路径开销:在同一任播IP地址的情况下,路由器根据用户的地理位置选择最短路径或最低延迟的路径,以减少数据传输时间
-
网络延迟:当路径开销类似时,路由器会优先选择网络延迟较低的路径。网络延迟可以通过探测或协议交换获得。
-
路径稳定性:路由协议会优先选择路径稳定性较高的节点,路径的不稳定性可能会导致路由频繁更改,从而增加网络开销。
-
路由协议的计算与更新:在BGP等动态路由协议中,节点定期交换路由信息,一旦有节点发生变化(如故障、延迟增加),路由表会根据新的路由哦条件进行更新
-
负载平衡机制:如果多个节点的延迟相似,路由器会分配请求给不同节点,以实现负载平衡。这种策略也用于避免某个节点出现瓶颈。
举个例子:互联网中的任播-----Google DNS服务
假设你在中国访问Google的公共DNS服务器(如IP地址8.8.8.8).Google在全球多个地点(美国、欧洲、亚洲等)部署了多个共享这个IP地址的DNS服务器,这些服务器都配置为任播节点。
- 当你请求访问8.8.8.8时,路由器会根据你的地理位置和当前的网络状况,选择距离最近或响应最快的服务器节点来处理请求。例如,你的请求会被引导到位于亚洲的一个GoogleDNS服务器,而不是到美国或欧洲的服务器。
- 这样做的好处是降低了请求延迟,用户可以更快的速度获得DNS解析结果,同时,入股亚洲的服务器出现故障,请求会自动转向其他位置的服务器,确保服务的高可用性。
也就是说:在互联网中的任播应用,通过全局BGP协议实现,将全球用户的请求引导至最近的服务器节点。
- 再举个例子就是
- 主句A在北京,而主机B在石家庄、C和D在上海、E在硅谷。所有节点BCDE都监听同一个任播地址239.255.255.250和端口8080.
- 当A发送UDP消息到任播地址239.255.255.250:8080时,路由协议会选择一个最优路径,将消息转发给其中距离最近的一个节点,而不是多个节点。
代码示例:
- 场景设定
假设网络已经配置支持任播
发送方主机A位于北京,向任播地址239.255.255.250发送消息,端口为8080
接收方主机(B、C、D、E)都监听239.255.255.250:8080上。
我们可以使用UDP套接字实现该功能,并且需要网络配置支持任播路径选择,比确保消息能正确到达距离最近的节点。
- 发送方代码(主机A)–sender.cpp
发送方会向任播IP地址239.255.255.250的端口8080发送UDP消息。
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
using namespace std;
#define ANYCAST_IP "239.255.255.250" //任播IP地址
#define PORT 8080
int main() {
int sockdf;
struct sockaddr_in servaddr;
//创建UDP套接字
sockdf = socket(AF_INET, SOCK_DGRAM, 0);
if (sockdf < 0) {
perror("socket create Failure!");
return -1;
}
//设置目标地址
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port=htons(PORT);
if(inet_pton(AF_INET, ANYCAST_IP, &servaddr.sin_addr)<=0)//任播地址
{
perror("inet_pton Failure!");
clsoe(sockdf);
return -1;
}
//准备消息内容
const char* message="Hello from A(BeiJing)";
int msg_len=strlen(message);
//发送消息
if(sendto(sockfd,message,msg_len,0,((struct sockaddr*)&servaddr),sizeof(servaddr))<0)
{
perror("sendto Failure!");
close(sockdf);
return -1;
}
cout<<"Message send to"<<ANYCAST_IP<<":"<<PORT<<endl;
//关闭套接字
close(sockdf);
return 0;
}
- 接收方代码(主机B/C/D/E)–receiver.cpp
接收方监听在任播IP地址239.255.255.250和端口8080上,等待来自发送方的UDP消息
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
using namespace std;
#define ANYCAST_IP "239.255.255.250" //任播IP地址
#define PORT 8080
int main()
{
int sockfd;
struct sockaddr_in servaddr,clientaddr;
//创建UDP套接字
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0)
{
cout << "create socket error" << endl;
return -1;
}
//设置地址复用选项,允许多个节点绑定到相同的端口
int opt=1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))
{
cout << "set socket option error" << endl;
close(sockfd);
return -1;
}
//设置服务器地址
memeset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr(ANYCAST_IP);
//绑定套接字来任播地址
if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
{
cout << "bind socket error" << endl;
close(sockfd);
return -1;
}
//接收数据
char buf[1024];
socklen_t len = sizeof(clientaddr);
cout<<"Receiver listening on "<<ANYCAST_IP<<":"<<PORT<<endl;
while(true)
{
int n = recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr *)&clientaddr,&len);
if(n<0)
{
perror("recvfrom");
break;
}
buf[n] = '\0';
cout<<"Received message :"<<buf<<"from "
<<inet_ntoa(clientaddr.sin_addr)<<":"<<ntohs(clientaddr.sin_port)<<endl;
}
//关闭套接字
close(sockfd);
return 0;
}
- 代码说明和重要的函数参数
-
发送方:通过UDP套接字向任播地址239.255.255.250:8080发送消息
-
接收方:通过UDP套接字绑定到相同的任播地址和端口号239.255.255.250:8080,监听并接收消息。
-
a. 在多个接收方节点(如B、C、D、E)上运行receiver.cpp,并确保网络配置支持任播
-
b. 在发送方节点A上运行sender.cpp发送消息
-
- 难点代码
//设置地址复用选项,允许多个节点绑定到相同的端口
int opt=1;
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))
{
cout << "set socket option error" << endl;
close(sockfd);
return -1;
}
(1)sockfd:这是套接字文件描述符,socket()函数返回的值。setsockopt将对这个套接字应用配置选项。
(2)SOL_SOCKET:这是套接字层的选项类型,用于指定选项在套接字级别(例如,SO_REUSEADDR、SO_KEEPALIVE等)。当你想要设置通用的套接字选项(而不是协议特定的选项)时,会将此参数设置为SOL_SOCKET。
(3)SO_REUSEADDR:这是具体的选项标志,表示地址重用。它允许一个本地端口可以被多个套接字绑定,特别是在以下场景中使用:
···
····服务器快速重启:如果服务器进程使用某个端口并意外终止,那么在短时间内重新启动该进程时,端口可能还被操作系统标记为“正在使用”,SO_REUSEADDR可以使端口立即可用。
···多播和任播:多个进程可用监听同一个端口,这对于多播和任播应用非常重要,因为多个服务器实例可能同时监听同一端口来接收数据。
···端口共享:允许多个套接字同时绑定到同一IP地址和端口,但其中只有一个会接收传入数据。
···&opt:这是指向opt变量的指针,表示SO_REUSEADDR的值。我们将其设置为1(即opt=1),表示启用端口重用。若将其设置为0,则表示禁用端口重用。
···sizeof(opt):指定opt的大小,用于告诉setsockopt函数opt变量的长度。
- 预期结果
在正确配置任播的情况下,只有一个接收方节点(例如距离A最近的B节点)会接收到消息,并在控制台上打印发送方A的消息内容。这是因为任播路由的设计会自动将消息引导到最优路径的单一节点。
五、各种播的比较
特性 | 单播(Unicast) | 广播(Broadcast) | 任播(Anycast) | 组播(Multicast) |
---|---|---|---|---|
地址类型 | A、B、C类IP地址 | D类地址(以255.255.255.255结尾) | A、B、C类IP地址 | D类地址(224.0.0.0-239.255.255.255) |
数据传输范围 | 一个发送者到一个接收者 | 一个发送者到同一网络内的所有接收者 | 一个发送者到多个节点之一,选择最近的一个节点 | 一个发送者到指定组内的多个接收者 |
数据传输效率 | 效率低,需要多个单独的传输 | 效率低,所有设备都接收,无论是否需要 | 效率较高,只发送到最优接收者 | 效率较高,只发送到订阅的接收者 |
应用场景 | 点对点通信,例如CS模型 | 局域网中的广播(如ARP请求) | CDN、DNS等分布式服务,将流量引导到最近节点 | 视频会议、IPTV、在线直播等 |
IP地址范围 | 常规单播IP地址(如A类、B类、C类) | 子网广播(如192.168.1.255),或全网广播 | 常规单播IP地址(如A类、B类、C类) | D类地址范围(224.0.0.0-239.255.255.255) |
路由实现 | 常规路由、根据目标单个IP地址 | 不路由,一般局限于局域网内 | 通过路由协议选择最近或最优的路径 | 依靠多播路由协议(如PIM) |
典型协议 | TCP、UDP | UDP | BGP(用于选择最近节点) | IGMP、PIM |
适用范围 | 适用于需要专用通信的场景 | 局限于局域网 | 广域网和局域网均适用,要求网络支持任播配置 | 局域网和广域网均适用 |
优势 | 通信明确、安全性高 | 易于实现,无需复杂配置 | 实现负载均衡、提高服务性能 | 节省带宽、优化数据传输效率 |
缺点 | 不适用于大规模接收者的通信 | 消耗带宽、增加不必要的网络流量 | 依赖路由协议支持,配置复杂 | 需要额外的组播路由配置 |