如何在 Erlang 中发送多播消息并重用端口?
我的程序有了一个良好的开端,这是我的第一个真正的 Erlang 程序。 我让它监听消息、读取消息并解析它们。我也把它寄给他们了。 困扰我的一件小事是我无法在端口 5353 上发送,我已经尝试了所有方法。 我机器上的所有其他应用程序都可以在端口 5353、SubEthaEdit、iTunes、iChat 上监听和发送。
解决方案必须在端口 5353 上广播发送,原因如下。
“如果收到的多播 DNS 查询中的源 UDP 端口不是端口 5353,这表明发起查询的客户端是 没有完全实现所有组播 DNS 的简单客户端。 在这种情况下,多播 DNS 响应者必须发送 UDP 响应 通过单播直接返回到客户端,到查询数据包的 源IP地址和端口。该单播响应必须是 传统的单播响应将由传统的 单播 DNS 服务器;例如,它必须重复查询 ID 和 查询包中给出的问题。
发送组播消息时都报告端口:5353。 我真的希望我的应用程序运行良好并做同样的事情,在端口 5353 上发送。 这是我现在的模块。
-module(zeroconf).
-include("zeroconf.hrl").
-export([open/0,start/0]).
-export([stop/1,receiver/0]).
-export([send/1]).
-define(ADDR, {224,0,0,251}).
-define(PORT, 5353).
send(Domain) ->
{ok,S} = gen_udp:open(0,[{broadcast,true}]), % I really want this Port to be 5353 :-(
% this doesn't complain or throw errors but it also doesn't work :-(
%{ok,S} = gen_udp:open(?PORT,[{reuseaddr,true}, {ip,?ADDR}, {broadcast,true},multicast_ttl,4}, {multicast_loop,false}, binary]),
P = #dns_rec{header=#dns_header{},qdlist=[#dns_query{domain=Domain,type=ptr,class=in}]},
gen_udp:send(S,?ADDR,?PORT,inet_dns:encode(P)),
gen_udp:close(S).
以下是一些输出的样子。
这是来自 SubEthaEdit 的查询,用于查找本地网络上的其他实例,请注意它显示端口:5353
From: {192,168,0,105}
Port: 5353
Data: {ok,{dns_rec,{dns_header,0,true,'query',true,false,false,false,false,0},
[],
[{dns_rr,"_see._tcp.local",ptr,in,0,0,
"jhr@Blackintosh._see._tcp.local",undefined,[],
false}],
[],[]}}
现在这是来自我的模块的查询,用于查找本地网络上的 iTunes 实例,注意它显示端口:59795 按照现在的代码,该端口是随机的。我真的希望它是 5353。
From: {192,168,0,105}
Port: 59795
Data: {ok,{dns_rec,{dns_header,0,false,'query',false,false,false,false,false,
0},
[{dns_query,"_daap._tcp.local",ptr,in}],
[],[],[]}}
有人对 UDP 多播有任何神秘的见解吗?更新以便我可以尝试接受答案。我想我就是做不到这一点。
I have gotten a good start on my program, my first REAL Erlang program.
I have it listening for messages, reading them and parsing them. I also have it sending them.
The one little thing that is bothering me is I can't SEND on Port 5353, I have tried everything.
All the other applications on my machine can listen AND send on port 5353, SubEthaEdit, iTunes, iChat.
The solution MUST broadcast send on port 5353 and here is why.
" If the source UDP port in a received Multicast DNS Query is not port
5353, this indicates that the client originating the query is a
simple client that does not fully implement all of Multicast DNS.
In this case, the Multicast DNS Responder MUST send a UDP response
directly back to the client, via unicast, to the query packet's
source IP address and port. This unicast response MUST be a
conventional unicast response as would be generated by a conventional
unicast DNS server; for example, it MUST repeat the query ID and the
question given in the query packet.
"
They all report Port: 5353 when sending multicast messages.
I really want my application to play nice and do the same thing, send on Port 5353.
Here is my module as it stands now.
-module(zeroconf).
-include("zeroconf.hrl").
-export([open/0,start/0]).
-export([stop/1,receiver/0]).
-export([send/1]).
-define(ADDR, {224,0,0,251}).
-define(PORT, 5353).
send(Domain) ->
{ok,S} = gen_udp:open(0,[{broadcast,true}]), % I really want this Port to be 5353 :-(
% this doesn't complain or throw errors but it also doesn't work :-(
%{ok,S} = gen_udp:open(?PORT,[{reuseaddr,true}, {ip,?ADDR}, {broadcast,true},multicast_ttl,4}, {multicast_loop,false}, binary]),
P = #dns_rec{header=#dns_header{},qdlist=[#dns_query{domain=Domain,type=ptr,class=in}]},
gen_udp:send(S,?ADDR,?PORT,inet_dns:encode(P)),
gen_udp:close(S).
Here is what some output looks like.
This is a QUERY from SubEthaEdit looking for other instances on the local network, notice that it says Port: 5353
From: {192,168,0,105}
Port: 5353
Data: {ok,{dns_rec,{dns_header,0,true,'query',true,false,false,false,false,0},
[],
[{dns_rr,"_see._tcp.local",ptr,in,0,0,
"jhr@Blackintosh._see._tcp.local",undefined,[],
false}],
[],[]}}
Now here is a QUERY from my module looking for instances of iTunes on the local network, notice it says Port: 59795
With the code the way it is now, that port is random. I really want it to be 5353.
From: {192,168,0,105}
Port: 59795
Data: {ok,{dns_rec,{dns_header,0,false,'query',false,false,false,false,false,
0},
[{dns_query,"_daap._tcp.local",ptr,in}],
[],[],[]}}
Does anyone have any arcane insight in to UDP multicast at all? Updating so I can try and accept an answer. I think I just can't do this.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
更新:好的,我已经找到了我认为可行的解决方案。关键点似乎与加入多播组有关。
(当然,请确保您没有运行DNS守护程序可能会发生冲突)
UPDATED: ok, I have found what I believe to be a working solution. The crucial point it seems relate to joining a multicast group.
( Of course, make sure that you are not running DNS daemon that might enter in conflict)
没有足够的代表来回复 Emil 帖子下的 {broadcast,true} 讨论,抱歉。
必须设置 SO_BROADCAST 套接字标志(我假设映射到该标志),否则 sendto(广播地址)将失败。这是一个安全措施,可防止无意广播的节目被滥用或出现错误。否则,安全程序将不得不尝试自行检查广播地址。
启用 SO_BROADCAST 不会阻止您发送非广播数据包。 (再次,假设 erlang 的东西只是直接映射到setsockopts;我不知道 erlang,只是网络!)
您可能想尝试 strace 来查看实际发生的系统调用。查找socket(),然后该文件描述符会发生什么。
don't have enough rep to reply to the {broadcast,true} discussion under emil's post, sorry.
The SO_BROADCAST socket flag (which I assume that maps to) must be set or sendto(a broadcast address) will fail. This is a safety catch to prevent abuse or errors with programs that didn't intend to broadcast. Otherwise secure programs would have to try to check for broadcast addresses themselves.
enabling SO_BROADCAST doesn't stop you from sending non-broadcast packets. (again, assuming erlang's stuff just maps directly to setsockopts; I don't know erlang, just networking!)
You might want to try strace to see what system calls actually happen. look for socket(), and then what happens to that file descriptor.
您尝试打开一个已经打开的套接字?不能使用同一个套接字来发送和接收吗?
You try to open a socket which is already open? Can't you use the same socket for sending and receiving?