组播接收器套接字中的重复数据包
以下 MulticastReceiver 实现中似乎存在错误。
在为 <224.0.25.46,13001> 创建两个实例时和<224.0.25.172,13001>,我在每个流中获得每个数据包两次。有什么指点吗?我的猜测是 REUSEADDR ?
class MulticastReceiverSocket {
protected:
const std::string listen_ip_;
const int listen_port_;
int socket_file_descriptor_;
public:
MulticastReceiverSocket ( const std::string & listen_ip,
const int listen_port )
: listen_ip_ ( listen_ip ), listen_port_ ( listen_port ),
socket_file_descriptor_ ( -1 )
{
/* create socket to join multicast group on */
socket_file_descriptor_ = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( socket_file_descriptor_ < 0 )
{ exit(1); }
/* set reuse port to on to allow multiple binds per host */
{
int flag_on = 1;
if ( ( setsockopt ( socket_file_descriptor_, SOL_SOCKET,
SO_REUSEADDR, &flag_on,
sizeof(flag_on) ) ) < 0 )
{ exit(1); }
}
McastJoin ( );
{
/* construct a multicast address structure */
struct sockaddr_in mcast_Addr;
bzero ( &mcast_Addr, sizeof(mcast_Addr) );
mcast_Addr.sin_family = AF_INET;
mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
mcast_Addr.sin_port = htons ( listen_port_ );
/* bind to specified port onany interface */
if ( bind ( socket_file_descriptor_, (struct sockaddr *) &mcast_Addr, sizeof ( struct sockaddr_in ) ) < 0 )
{ exit(1); }
}
}
void McastJoin ( )
{
/* construct an IGMP join request structure */
struct ip_mreq mc_req;
inet_pton ( AF_INET, listen_ip_.c_str(), &(mc_req.imr_multiaddr.s_addr) );
mc_req.imr_interface.s_addr = htonl(INADDR_ANY);
/* send an ADD MEMBERSHIP message via setsockopt */
if ( ( setsockopt ( socket_file_descriptor_, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(void*) &mc_req, sizeof(mc_req))) < 0)
{
printf ("setsockopt() failed in IP_ADD_MEMBERSHIP %s\n", listen_ip_.c_str() );
exit(1);
}
}
inline int ReadN ( const unsigned int _len_, void * _dest_ )
{
if ( socket_file_descriptor_ != -1 )
{
return recvfrom ( socket_file_descriptor_, _dest_, _len_, 0, NULL, NULL );
}
return -1;
}
请指教,当然,请指出任何可以进行的改进、优化。
There seems to be a bug in the following MulticastReceiver implementation.
On creating two instances for <224.0.25.46,13001> and <224.0.25.172,13001>, I get each packet twice in each stream. Any pointers ? My guess is REUSEADDR ?
class MulticastReceiverSocket {
protected:
const std::string listen_ip_;
const int listen_port_;
int socket_file_descriptor_;
public:
MulticastReceiverSocket ( const std::string & listen_ip,
const int listen_port )
: listen_ip_ ( listen_ip ), listen_port_ ( listen_port ),
socket_file_descriptor_ ( -1 )
{
/* create socket to join multicast group on */
socket_file_descriptor_ = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( socket_file_descriptor_ < 0 )
{ exit(1); }
/* set reuse port to on to allow multiple binds per host */
{
int flag_on = 1;
if ( ( setsockopt ( socket_file_descriptor_, SOL_SOCKET,
SO_REUSEADDR, &flag_on,
sizeof(flag_on) ) ) < 0 )
{ exit(1); }
}
McastJoin ( );
{
/* construct a multicast address structure */
struct sockaddr_in mcast_Addr;
bzero ( &mcast_Addr, sizeof(mcast_Addr) );
mcast_Addr.sin_family = AF_INET;
mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
mcast_Addr.sin_port = htons ( listen_port_ );
/* bind to specified port onany interface */
if ( bind ( socket_file_descriptor_, (struct sockaddr *) &mcast_Addr, sizeof ( struct sockaddr_in ) ) < 0 )
{ exit(1); }
}
}
void McastJoin ( )
{
/* construct an IGMP join request structure */
struct ip_mreq mc_req;
inet_pton ( AF_INET, listen_ip_.c_str(), &(mc_req.imr_multiaddr.s_addr) );
mc_req.imr_interface.s_addr = htonl(INADDR_ANY);
/* send an ADD MEMBERSHIP message via setsockopt */
if ( ( setsockopt ( socket_file_descriptor_, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(void*) &mc_req, sizeof(mc_req))) < 0)
{
printf ("setsockopt() failed in IP_ADD_MEMBERSHIP %s\n", listen_ip_.c_str() );
exit(1);
}
}
inline int ReadN ( const unsigned int _len_, void * _dest_ )
{
if ( socket_file_descriptor_ != -1 )
{
return recvfrom ( socket_file_descriptor_, _dest_, _len_, 0, NULL, NULL );
}
return -1;
}
Please advise, and of course, please point out any improvements, optimizations that can be made.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
替换
为
它对我的帮助(Linux),对于每个应用程序,我从一个端口上的单独的 mcast 组接收单独的 mcast 流。
您还可以查看 VLC 播放器源代码,它在一个端口上显示来自不同 mcast 组的许多 mcast iptv 频道,但我不知道它如何分隔频道。
Replace
with
it's help for me (linux), for each application i receive separate mcast stream from separate mcast group on one port.
Also you can look into VLC player source, it show many mcast iptv channel from different mcast group on one port, but i dont know, how it separetes channel.
您可以采取的一种方法是明智地选择如何加入组,因此不要创建套接字,而是绑定(使用 REUSEADDR)然后为每对 ip、端口加入组,而只构建一个套接字并绑定到给定的端口,然后在同一个套接字上发出多个 IGMP 加入。
即在您的情况下,仅创建一个套接字,每个端口绑定一次,但您加入多个组。唯一的区别是,当您发出读取调用时,您将从一个或另一组获得一个数据包,并且您需要数据包中有足够的数据以便您区分。
One approach you can take is to be smart about how you join the groups, so rather than create a socket, bind (with REUSEADDR) and then join group for each pair of ip, port, only construct a single socket and bind to a given port, and then issue multiple IGMP joins on the same socket.
i.e. in your case, only one socket is created, you bind once per port, but you join multiple groups. The only difference is that when you issue the read call, you will get a packet from one or the other group and you need to have enough data in the packet to allow your to distinguish.
我猜这是因为多个接口(您在
INADDR_ANY
上加入组)。尝试指定确切的接口。通过ioctl(2 获取接口地址)
与 <代码>SIOCGIFADDR。使用netstat -ng
检查您在哪个界面加入了哪些组。I'm guessing this is because of multiple interfaces (you join the group on
INADDR_ANY
). Try specifying exact interface. Get the interface address viaioctl(2)
withSIOCGIFADDR
. Check what groups you joined on what interface withnetstat -ng
.这是Linux路由的一个功能,每个会话都需要一个唯一的端口/多播组,只要端口匹配,Linux就会转发任何内容,例如广播数据包。令人惊讶的是,Windows 没有这种症状,这可能就是它速度较慢的原因。
许多商业中间件软件包强制执行此兼容性要求,例如 TIBCO 的 Rendezvous 不允许重复使用相同的端口或组。
It's a feature of Linux routing, you need a unique port/multicast group for each session, Linux will forward on anything as long as the port matches, for example broadcast packets too. Windows surprisingly doesn't have this symptom, which is presumably why it is slower.
Many commercial middleware packages enforce this requirement for compatibility, for example TIBCO's Rendezvous will not permit reusing of the same port or group.
您是否尝试过关闭环回?我发现,如果我有一个合理的 TTL,则不需要环回即可获得单个数据包,至少在使用 SO_REUSEPORT 时是这样:
如果我将环回打开(默认情况下如此),我也会收到双数据包。
Have you tried just turning loopback off? I find that if I have a reasonable TTL, no loopback is required to get a single, at least when using SO_REUSEPORT:
If I leave loopback turned on--as it is by default--I get double packets too.