UDP:从所有网络接口读取数据

Posted

技术标签:

【中文标题】UDP:从所有网络接口读取数据【英文标题】:UDP: Read data from all network interfaces 【发布时间】:2013-02-22 07:44:30 【问题描述】:

我有以下代码来读取来自网络的多播消息,用于指定的 IP+端口

private static void ReceiveMessages(int port, string ip, CancellationToken token)

    Task.Factory.StartNew(() =>
        
            using (var mUdpClientReceiver = new UdpClient())
            
                var mReceivingEndPoint = new IPEndPoint(IPAddress.Any, port);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);

                while (!token.IsCancellationRequested)
                
                    byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);

                    Console.WriteLine("Message received from 0 ",mReceivingEndPoint);
                
            
        );

我有两个网络适配器,我有来自这个多播 ip+端口的数据(由两个监控每个网络适配器的wireshark 实例确认)。我在wireshark上看到两个网卡的这些端口+Ip)上都有很多流量。

问题是在我的控制台上,我只能看到来自一张网卡的消息。

我仔细检查了 netstat,我的端口上没有任何其他软件正在监听:

那么为什么我只能从我的两张网卡中的一张获得流量?

编辑

我什至尝试了以下方法:

private static void ReceiveMessages(int port, string ip, CancellationToken token, IEnumerable<IPAddress> ipAddresses)

    foreach (IPAddress ipAddress in ipAddresses)
    
        IPAddress ipToUse = ipAddress;
        Task.Factory.StartNew(() =>
        
            using (var mUdpClientReceiver = new UdpClient())
            

                var mReceivingEndPoint = new IPEndPoint(ipToUse, port);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                mUdpClientReceiver.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
                mUdpClientReceiver.ExclusiveAddressUse = false;
                mUdpClientReceiver.Client.Bind(mReceivingEndPoint);
                mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255);
                Console.WriteLine("Starting to listen on "+ipToUse);
                while (!token.IsCancellationRequested)
                
                    byte[] receive = mUdpClientReceiver.Receive(ref mReceivingEndPoint);

                    Console.WriteLine("Message received from 0 on 1",  mReceivingEndPoint,ipToUse);
                
            
        );
    

我看到两次“开始监听 theCorrectIP”(对于我的两个 IP),但它仍然只显示来自一张网卡的数据。

编辑 2

我确实也注意到了一些奇怪的东西。如果我禁用接收所有数据的接口,然后启动软件,我现在从另一个接口获取数据。如果我再次激活接口并重新启动软件,我仍然会在未激活的卡上获得流量。

而且我确定我有响应我的设备,这些设备只连接到一个网络(不是两个)

编辑 3

另一件事:如果我从我(本地主机)发送一条消息,在我拥有的所有网卡上,我会看到它们出现在我的两个网络接口上。但是,如果我启动我的程序两次,只有第一个程序会收到消息,而不是第二个。

编辑 4

附加信息,在第一条评论之后: 我有两张以太网卡,一张带有10.10.24.78 ip,另一张带有10.9.10.234 ip。 发送数据的不是我,而是网络片段(带有此 ip 的端口5353 是用于 mDNS 的已知多播地址,所以我应该接收来自打印机、iTunes、Mac 和我们创建的其他一些软件的流量)。数据在ip上多播 224.0.0.251 和端口5353

这是一个代码,您可以使用它在多个 IP 上发送数据,但就像我描述的那样,如果您在本地启动它几乎可以工作(除了只有一个本地客户端接收消息)。

private static void SendManuallyOnAllCards(int port, string multicastAddress, IEnumerable<IPAddress> ipAddresses)

    foreach (IPAddress remoteAddress in ipAddresses)
    
        IPAddress ipToUse = remoteAddress;
        using (var mSendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
        
            mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership,
                                        new MulticastOption(IPAddress.Parse(multicastAddress)));
            mSendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
            mSendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

            var ipep = new IPEndPoint(ipToUse, port);
            //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(multicastAddress), port);
            mSendSocket.Bind(ipep);
            mSendSocket.Connect(ipep);


            byte[] bytes = Encoding.ASCII.GetBytes("This is my welcome message");
            mSendSocket.Send(bytes, bytes.Length, SocketFlags.None);
        
    

编辑 5 这是我的route print(不知道那个命令)的结果,在我的两个IP上,我总是在10.9.10.234上接收数据

编辑 6

我尝试了其他几件事:

    使用套接字而不是 UdpClient 接收 --> 无效 在阅读器上设置一些额外的socketOption(DontRoute =1,Broadcast=1)-->没用 指定读卡器Socket必须使用的MulticastInterface(使用socketOption MulticastInterface)-->没用

【问题讨论】:

您的接口是什么(以太网、eth/wlan)?您在这些接口上有哪些 IP?如果您能分析一下您是如何发送多播消息的,那将会更有帮助。 @PCoder :感谢您的评论,我在最初的问题上添加了一些细节(编辑 4) 你能告诉我们你的route print命令的输出吗? @PCoder: 有什么可以帮助的,我真的很绝望(而且我不是网络专家:()。我用结果编辑了问题 我同意你的问题确实很棘手,我也没有解决方案:(。我发现了一个与你的问题非常相似的问题,也许这会有所帮助。http://linux.derkeiler.com/Newsgroups/comp.os.linux.networking/2009-09/msg00223.html 【参考方案1】:

我遇到了同样的问题,我想从我的所有网络接口接收多播。正如 EJP 已经说过的,您需要在 UdpClient 上为所有网络接口调用 JoinMulticastGroup(IPAddress multicastAddr, IPAddress localAddress)

int port = 1036;
IPAddress multicastAddress = IPAddress.Parse("239.192.1.12");

client = new UdpClient(new IPEndPoint(IPAddress.Any, port));

// list of UdpClients to send multicasts
List<UdpClient> sendClients = new List<UdpClient>();

// join multicast group on all available network interfaces
NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

foreach (NetworkInterface networkInterface in networkInterfaces)

    if ((!networkInterface.Supports(NetworkInterfaceComponent.IPv4)) ||
        (networkInterface.OperationalStatus != OperationalStatus.Up))
    
        continue;
    

    IPInterfaceProperties adapterProperties = networkInterface.GetIPProperties();
    UnicastIPAddressInformationCollection unicastIPAddresses = adapterProperties.UnicastAddresses;
    IPAddress ipAddress = null;

    foreach (UnicastIPAddressInformation unicastIPAddress in unicastIPAddresses)
    
        if (unicastIPAddress.Address.AddressFamily != AddressFamily.InterNetwork)
        
            continue;
        

        ipAddress = unicastIPAddress.Address;
        break;
    

    if (ipAddress == null)
    
        continue;
    

    client.JoinMulticastGroup(multicastAddress, ipAddress);

    UdpClient sendClient = new UdpClient(new IPEndPoint(ipAddress, port));
    sendClients.Add(sendClient);

我还创建了一个 UdpClients 列表,以便我可以在所有网络接口上发送我的多播。

【讨论】:

谢谢,我觉得这很有帮助【参考方案2】:

我终于找到了方法!

事实上,如果我保留完全相同的代码,但将它与异步方法一起使用,它就可以工作!!!我只是不明白为什么它不适用于同步方法(如果有人知道,欢迎您告诉我:))

由于我在这方面浪费了 3 天,我认为值得举一个例子:

private static void ReceiveAsync(int port, string address, IEnumerable<IPAddress> localAddresses)

    IPAddress multicastAddress = IPAddress.Parse(address);
    foreach (IPAddress localAddress in localAddresses)
    
        var udpClient = new UdpClient(AddressFamily.InterNetwork);
        udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpClient.Client.Bind(new IPEndPoint(localAddress, port));
        udpClient.JoinMulticastGroup(multicastAddress, localAddress);
        udpClient.BeginReceive(OnReceiveSink,
                               new object[]
                                   
                                       udpClient, new IPEndPoint(localAddress, ((IPEndPoint) udpClient.Client.LocalEndPoint).Port)
                                   );
    

还有异步方法:

private static void OnReceiveSink(IAsyncResult result)

    IPEndPoint ep = null;
    var args = (object[]) result.AsyncState;
    var session = (UdpClient) args[0];
    var local = (IPEndPoint) args[1];

    byte[] buffer = session.EndReceive(result, ref ep);
    //Do what you want here with the data of the buffer

    Console.WriteLine("Message received from " + ep + " to " + local);

    //We make the next call to the begin receive
    session.BeginReceive(OnReceiveSink, args);

希望对你有帮助 ;)

【讨论】:

【参考方案3】:

您需要通过所有可用接口加入多播组。默认情况下,传出的 IGMP JOIN 消息将根据单播路由表进行路由,单播路由表将使用访问该路由的任何 NIC 将其通过“最便宜”路由发送出去。如果您的多播组可以通过多个这些路由获取,则需要迭代。

【讨论】:

我不确定是否理解,为所有 UdpClient 完成的 mUdpClientReceiver.JoinMulticastGroup(IPAddress.Parse(ip), 255); 正在这样做,不是吗?如果没有,我该怎么做? 您必须遍历本地接口的 IP 地址并通过每个接口加入。 我想你没有看到我的第二个代码引用,这正是我正在做的。 @J4N 别这样。这正是您现在所做的,在您的答案中的代码中,该代码是在我发布此内容几个小时后发布的。关键是遍历本地 IP 地址并使用接受本地地址的 JoinMulticastGroup() 形式。你不是也不是,在你问题的代码中这样做,这就是我要回答的。 @J4N 不,不同的是你现在正在做我一开始告诉你做的事情,通过所有本地接口的所有地址加入多播组,通过使用我上面提到的 JoinMulticastGroup() 的形式,将本地地址作为参数。没有其他方法可以做到这一点。您的第二段代码没有这样做,因此不构成已经按照我所说的进行。使用异步 API 与它无关。在 Async API 出现之前,您无法真正相信多宿主多播不起作用。十年前我就这样做了。

以上是关于UDP:从所有网络接口读取数据的主要内容,如果未能解决你的问题,请参考以下文章

TCP和UDP套接字编程 (java实现)

网络编程

Linux网络基础

UDP千兆网之TCP/IP协议基础

接口测试之网络其他层次

网络篇套接字编程