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

时间:2013-03-07 07:39:58

标签: c# sockets network-programming udp udpclient

我有以下代码来读取来自网络的多播消息,用于指定的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进行了双重检查,我的端口上没有任何其他软件监听: enter image description here

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

修改

我甚至尝试了以下内容:

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

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

编辑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上的数据 enter image description here

修改6

我尝试了其他几件事:

  1. 使用套接字代替UdpClient接收 - &gt;没工作
  2. 在阅读器上设置一些额外的socketOption(DontRoute = 1,Broadcast = 1) - &gt;没有工作
  3. 指定阅读器套接字必须使用的MulticastInterface(使用socketOption MulticastInterface) - &gt;没用?

3 个答案:

答案 0 :(得分:6)

我终于找到了怎么做!

事实上,如果我保持完全相同的代码,但使用异步方法,它工作!我只是无法理解为什么它不能与同步方法一起工作(如果有人知道,欢迎你告诉我:))

由于我已经失去了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);
}

我希望有所帮助;)

答案 1 :(得分:3)

我有同样的问题,我想从我的所有网络接口接收多播。正如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 :(得分:2)

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