260 likes | 398 Views
广播与多播. 单播 / 广播 / 多播通信. 单播通信:网络中单一的源节点发送封包到单一的目的节点。 广播通信:将封包从一个节点发送到所有其他节点。 多播通信:将封包从一个节点发送到其他多个网络节点的集合。. 广播通信. 广播、多播仅适用于 UDP 协议 广播的负面作用是明显的:多个进程都发送广播数据,网络性能会受到影响。 几乎所有路由器都不转发广播数据,广播程序仅应用于本地子网。. 发送广播数据. SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0); // 有效 SO_BROADCAST 选项
E N D
单播 / 广播 / 多播通信 • 单播通信:网络中单一的源节点发送封包到单一的目的节点。 • 广播通信:将封包从一个节点发送到所有其他节点。 • 多播通信:将封包从一个节点发送到其他多个网络节点的集合。
广播通信 • 广播、多播仅适用于UDP协议 • 广播的负面作用是明显的:多个进程都发送广播数据,网络性能会受到影响。 • 几乎所有路由器都不转发广播数据,广播程序仅应用于本地子网。
发送广播数据 SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0); // 有效SO_BROADCAST选项 BOOL bBroadcast = TRUE; ::setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)); // 设置广播地址,这里的广播端口号(电台)是4567 SOCKADDR_IN bcast; bcast.sin_family = AF_INET; bcast.sin_addr.s_addr = INADDR_BROADCAST; // ::inet_addr("255.255.255.255"); bcast.sin_port = htons(4567); // 发送广播 printf(" 开始向4567端口发送广播数据... \n \n"); char sz[] = "This is just a test. \r\n"; while(TRUE) { ::sendto(s, sz, strlen(sz), 0, (sockaddr*)&bcast, sizeof(bcast)); ::Sleep(5000); }
套接字选项 • 套接字选项和I/O控制命令用于改变套接字的默认行为 • 主要的函数: getsockopt(SOCKET s,int level,int optname,char* optval,int* optlen); setsockopt(SOCKET s,int level,int optname,char* optval,int optlen); • s:套接字句柄 • level:指定选项定义在哪个级别 • optname:套接字选项名称 • optval:指定一个缓冲区,用于选项的值 • optlen:optval所指缓冲区的大小
level • 网络是分层的,每层上又有多个协议,因此套接字选项有不同的级别 • 常见级别: • SOL_SOCKET(对应应用层) • IPPROTO_TCP(对应传输层的TCP协议 • IPPROTO_UDP (对应传输层的UDP协议) • IPPROTO_IP (对应网络层的IP协议)
optname • 各级别的选项不同,同一级别不同协议的选项也可能不同
SOL_SOCKET级别的选项 • SO_BROADCAST • BOOL型 • 设置套接字传输和接收广播消息。如果给定套接字已进行过设置,则返回TRUE 。该选项只对不是SOCKET_STREAM类型的套接字有效 • SO_REUSEADDR • BOOL型 • 设置为TRUE,套接字可以被绑定到一个已经使用的本地地址。 • 不能将两个监听套接字绑定到相同的本地地址
IPPROTO_IP级别的选项 • IP_TTL • 设置和获取IP头中的TTL参数 • IP_ADD_MEMBERSHIP • 加入多播组 • IP_DROP_MEMBERSHIP • 离开多播组
程序实例 • BOOL bBroadcast = TRUE; setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)); • level: SOL_SOCKET • optname: SO_BROADCAST • optval: TRUE,但需要进行类型转换 • optlen: 一个bool值所需要的缓冲区大小
广播地址 • SOCKADDR_IN bcast; bcast.sin_family = AF_INET; bcast.sin_addr.s_addr = INADDR_BROADCAST; // ::inet_addr("255.255.255.255"); bcast.sin_port = htons(4567); • INADDR_BROADCAST此处等价于特殊地址“255.255.255.255” • 此地址用于对所处网络几乎没有了解的条件下 • 一般的广播地址:IP地址中主机号为全1
接收广播数据 • 接收程序只需要将套接字绑定到广播端口,即可接收同一子网内的该端口上的广播数据 • SOCKADDR_IN sin; sin.sin_family = AF_INET; sin.sin_addr.S_un.S_addr = INADDR_ANY; sin.sin_port = ::ntohs(4567); if(::bind(s, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) { printf(" bind() failed \n"); return; }
recv()与recvfrom() • recvfrom()还可以得到数据来源端的地址 • 示例程序中用recv()也可以完成功能
多播(Multicast)通信 • 广播的负面作用是显而易见的,多播是将封包发送到多个节点的更为可行的方法
多播地址 • 发送多播数据需要一个多播地址。每个多播地址代表一个多播组。 • 多播地址的范围 224.0.0.0 ~ 239.255.255.255
组管理协议(IGMP) • IPV4用于管理多播客户和他们之间关系的协议 • IGMP用于通知路由器对指定组的数据感兴趣 • 终端加入多播组时,将指定TTL值,指明终端的多播程序想要经过多少个路由器发送和接收数据
加入多播组 • ip_mreq mcast; mcast.imr_interface.S_un.S_addr = INADDR_ANY; mcast.imr_multiaddr.S_un.S_addr = inet_addr("234.5.6.7"); // 多播地址为234.5.6.7 setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)); • 通过设置套接字选项,加入多播组
ip_mreq结构 Typedef struct { //多播地址 struct in_addr imr_multiaddr; //将要加入或离开多播组的本地地址 struct in_addr imr_interface; }
离开多播组 • ip_mreq mcast; mcast.imr_interface.S_un.S_addr = dwInterFace; mcast.imr_multiaddr.S_un.S_addr = dwMultiAddr; setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&mcast, sizeof(mcast)); • 通过设置套接字选项,离开多播组
接收多播数据 • 接收多播数据,必须加入特定的多播组,并将套接字绑定到特定的端口。 • char buf[1280]; int nAddrLen = sizeof(si); … … int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
发送多播数据 • 向特定的组发送多播数据,并不必要加入这个组。 • 准备好多播数据,准备好多播地址,即可调用sendto()发送 • char sz[] = "This is just a test. \r\n"; while(TRUE) { ::sendto(s, sz, strlen(sz), 0, (sockaddr*)&bcast, sizeof(bcast)); ::Sleep(5000); }
带有源地址的IP多播 • 带源地址的IP多播允许加入组时指定要接收哪些成员的数据。 • 可以使用“包含”和“排除”两种方式加入组 • “包含”方式:指定多个有效的源地址,套接字仅接收来自这些源地址的数据 • “排除”方式:指定多个有效的源地址,套接字不接收来自这些源地址的数据
“包含”方式 • 使用IP_ADD_SOURCE_MEMBERSHIP选项配置套接字,加入多播组,并指定数据的源地址 • 选项的值为一个ip_mreq_source结构 • 使用IP_DROP_SOURCE_MEMBERSHIP从集合中移除源地址
ip_mreq_source结构 struct ip_mreq_source { //多播组地址 struct in_addr imr_multiaddr; //指定的源地址 struct in_addr imr_sourceaddr; //本地地址接口 struct in_addr imr_interface; }
“排除”方式 • 使用IP_ADD_MEMBERSHIP选项加入组,此时使用的就是“排除”方式 • 使用IP_BLOCK_SOURCE选项来指定要派出的源地址