我正在通过VPN试验IPv6 UDP组播。我尝试了以下代码:
const dgram = require('dgram');
let sock = dgram.createSocket('udp6', {
reuseAddr: true
});
sock.on('message', (data, source) => {
console.log('on message', arguments);
});
sock.bind('36912', '2620:9b::1944:e598', () => {
sock.addMembership('ff02::1:3', '2620:9b::1944:e598');
});
setInterval(() => {
let buf = Buffer.from((new Date()).toString());
sock.send(buf, 0, buf.length, 36912, 'ff02::1:3');
}, 500);
脚本运行,我看到使用Wireshark发送/接收的数据包,但两端都没有在控制台中显示它们。
我做错了什么?什么是使用IPv6发送和接收基本多播的方法?
答案 0 :(得分:7)
在IPv6中,有一个地址的scope_id概念,它应该指示IP地址的上下文,通常只是指它可以访问的接口。虽然范围具有特定于操作系统的名称,但每个范围仅转换为接口编号,0通常表示系统的默认名称。
在IPv6多播中,此scope_id直接提供给IP_ADD_MEMBERSHIP和IP_MULTICAST_IF,而不是像IPv4那样提供与接口关联的IP。
节点(通过libuv)通过在"接口地址"中查找scope_id,在addMembership
中为您隐藏这种差异。你提供。
不幸的是,从一个IP开始并获得一个范围并没有多大意义(范围的重点是IP可以在不同的范围内有不同的用途。)所以libuv只能够如果您使用%[scope]
格式在地址末尾明确提供,请填写范围。
解决这个问题的方法似乎是:
sock.bind('36912', '::', () => {
sock.addMembership('ff02::1:3', '::%eth2');
...
sock.send(buf, 0, buf.length, 36912, 'ff02::1:3%eth2');
其中:
在绑定中使用::
(或没有地址)是必要的,因为您要将使用需要普通地址的send过滤多播地址的接收组合起来。
使用%[iface#]
强制使用此界面的范围#。
addMembership的第二个参数实际上可以从任何地址开始,因为我们强制范围,其余的被丢弃。
通常发送套接字是分开的,并且给定一个不同的端口或一个匿名端口,因为您要么可以配置,要么因为套接字太相似而面临EADDRINUSE错误的危险。
答案 1 :(得分:1)
我按照此答案中的步骤操作,但仍然无法使IPv6多播工作。事实证明我设置socket.setMulticastLoopback(false)
来过滤掉来自节点本身的消息,这对于IPv4来说效果很好,但是阻止了IPv6的所有消息。删除此项修复了问题并且消息开始正确显示,无需过滤。