如何在Erlang中发送组播消息并重用端口?

7
我已经开始编写我的第一个真正的Erlang程序,涉及IT技术。我让它监听消息、读取并解析它们,还可以发送它们。唯一困扰我的小问题是,我无法在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组播有深入的了解吗?更新一下以便我可以尝试并接受一个答案。我想我只是无法做到这一点。

3个回答

3

更新:好的,我已经找到了一个我认为是有效的解决方案。关键点似乎与加入组播组有关。

{ok, Socket} = gen_udp:open(Port=5353, [binary, {active, false}, {reuseaddr, true},
                                        {ip, Addr}, {add_membership, {Addr, IAddr}}]).
  1. 地址:多播组(例如{224, 0, 0, 251})
  2. IAddr是本地IP接口(例如可以使用默认{0,0,0,0})

当然,确保您没有运行可能会发生冲突的DNS守护程序。


1

抱歉,我的声望不够,在Emil的帖子下回复{broadcast,true}讨论。

我认为SO_BROADCAST套接字标志(我假设它是这样映射的)必须设置,否则sendto(广播地址)将失败。这是一种安全措施,以防止滥用或错误地使用未打算广播的程序。否则,安全程序将不得不尝试自己检查广播地址。

启用SO_BROADCAST并不会阻止您发送非广播数据包。(再次声明,假设erlang的东西直接映射到setsockopts;我不懂erlang,只懂网络!)

您可能想尝试strace以查看实际发生的系统调用。查找socket(),然后查找该文件描述符的情况。


0

你试图打开一个已经打开的套接字?难道你不能使用同一个套接字进行发送和接收吗?


我不这么认为,因为我需要传递 {broadcast,true} 选项进行发送。 - user177800
发送消息时,使用inet:setopts/2更改{broadcast, true/false}不是可能的吗? - emil
我无法通过任何设置或不设置来发送消息。 - user177800

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接