IP多播
时间:2022-04-22
本文章向大家介绍IP多播,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1 多播地址
IP多播地址采用D类IP地址确定多播的组,地址范围是224.0.0.0 到 239.255.255.255.
2 组管理协议(IGMP)
两个多播节点之间的所有路由器必须支持IGMP协议
任何没有开启IGMP的路由器仅简单的丢弃接收到的多播数据
3 使用IP多播
1 加入离开组
IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP
下面的代码展示了如何 加入组:
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 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_DROP_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
2 接收多播数据
主机在接收IP多播数据之前,必须成为IP多播组的成员。为了接收发送到特定端口的多播封包,有必要绑定到那个本地端口,而不是显示的指定本地地址
如果套接字使用SO_REUSEADDR选项,就可以不止一个进程可以绑定到UDP端口
如下代码所示:
BOOL bReuse = TRUE;
::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));
如此一来,每个来到这个共享端口的多播或者广播UDP封包都会发送给所绑定此端口的套接字
下面是接收多播封包的代码:
void main()
{
SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0);
// 允许其它进程使用绑定的地址
BOOL bReuse = TRUE;
::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));
// 绑定到4567端口
sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(4567);
si.sin_addr.S_un.S_addr = INADDR_ANY;
::bind(s, (sockaddr*)&si, sizeof(si));
// 加入多播组
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr = INADDR_ANY;
mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("192.168.0.1"); // 多播地址为234.5.6.7
::setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
// 接收多播组数据
printf(" 开始接收多播组上的数据... n");
char buf[1280];
int nAddrLen = sizeof(si);
while(TRUE)
{
int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
if(nRet != SOCKET_ERROR)
{
buf[nRet] = ' ';
printf(buf);
}
else
{
int n = ::WSAGetLastError();
break;
}
}
}
3 带源地址的IP多播
带源地址的IP多播允许加入组时,指定要接收哪些成员的数据
1 包含方式:指定N个有效的源地址,套接字仅接收来自这些源地址的数据
2 排除方式:指定N个有效的源地址,套接字将接受这些源地址之外的数据
SOCKET s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 本地接口
SOCKADDR_IN localif;
localif.sin_family = AF_INET;
localif.sin_port = htons(5150);
localif.sin_addr.s_addr = htonl(INADDR_ANY);
::bind(s, (SOCKADDR *)&localif, sizeof(localif));
// 设置ip_mreq_source结构
struct ip_mreq_source mreqsrc;
mreqsrc.imr_interface.s_addr = inet_addr("192.168.0.46");
mreqsrc.imr_multiaddr.s_addr = inet_addr("234.5.6.7");
// 添加源地址218.12.255.113
mreqsrc.imr_sourceaddr.s_addr = inet_addr("218.12.255.113");
::setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&mreqsrc, sizeof(mreqsrc));
// 添加源地址
mreqsrc.imr_sourceaddr.s_addr = inet_addr("218.12.174.222");
::setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&mreqsrc, sizeof(mreqsrc));
移除地址可以使用IP_DROP_MEMBERSHIP选项
全部试验源码:
sender.cpp:
///////////////////////////////////
// sender.cpp文件
#include "initsock.h"
#include "stdio.h"
#include <windows.h>
CInitSock theSock;
void main()
{
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 = ::inet_addr("255.255.255.255");
bcast.sin_port = htons(4567);
// 发送广播
char sz[] = "this is xingoo 123. rn";
while(TRUE)
{
::sendto(s, sz, strlen(sz), 0, (sockaddr*)&bcast, sizeof(bcast));
::Sleep(1000);
}
}
recv.cpp:
#include "Initsock.h"
#include <stdio.h>
#include <windows.h>
#include <Ws2tcpip.h>
// 初始化Winsock库
CInitSock theSock;
void main()
{
SOCKET s = ::socket(AF_INET, SOCK_DGRAM, 0);
// 允许其它进程使用绑定的地址
BOOL bReuse = TRUE;
::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(BOOL));
// 绑定到4567端口
sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = ::ntohs(4567);
si.sin_addr.S_un.S_addr = INADDR_ANY;
::bind(s, (sockaddr*)&si, sizeof(si));
// 加入多播组
ip_mreq mcast;
mcast.imr_interface.S_un.S_addr = INADDR_ANY;
mcast.imr_multiaddr.S_un.S_addr = ::inet_addr("192.168.0.1"); // 多播地址为234.5.6.7
::setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast));
// 接收多播组数据
printf(" 开始接收多播组上的数据... n");
char buf[1280];
int nAddrLen = sizeof(si);
while(TRUE)
{
int nRet = ::recvfrom(s, buf, strlen(buf), 0, (sockaddr*)&si, &nAddrLen);
if(nRet != SOCKET_ERROR)
{
buf[nRet] = ' ';
printf(buf);
}
else
{
int n = ::WSAGetLastError();
break;
}
}
}
运行结果:
- 为WordPress开启Nginx缩略图功能,七牛从此陌路
- 为网站开启Nginx缓存加速,支持html伪静态页面
- 解决WordPress升级4.2后调用国外图片导致大量404请求的问题
- JS代码实现浏览器网页标题的动态切换,略微提高网站粘性
- Go-List
- 分享张戈博客自用的php网址在线转换二维码的API源码
- zabbix agentd客户端插件Shell一键自动安装脚本
- SendCloud邮件队列状态和已使用额度的Python监控脚本
- linux/scp命令报“bash: scp: command not found lost connection”错误的解决办法
- bat/cmd批处理连接SqlServer数据库查询脚本
- 一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
- 解决mstsc无法连接问题:由于没有远程桌面授权服务器可以提供许可证…
- Apache/Nginx伪静态规则匹配http://出现的问题与解决
- 微信文件微起底
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法