はじめに
NIC を指定して Multicast を受信する方法をご紹介します。
複数の NIC(Network Interface Card)が実装されているパソコンが増えてきており、
情報を探しておられる方も多いはず。
Socket をローカルホストのエンドポイントへ Bind するだけではダメで、
最初に見つかった NIC を使って IGPM(マルチキャストグループ参加)を投げてしまう。
C# で書いていて、少しハマったので備忘録として本記事を記載します。
ネットワーク構成

Windows マシンが 2 つの NIC を搭載していて、それぞれ
- NIC1:192.168.10.0/24
- NIC2:192.168.56.0/24
のサブネットに属しているネットワーク構成です。
NIC2 の方のサブネットにネットワークカメラが接続されており、
そのカメラがマルチキャストでストリーム配信している状態です。

Multicast を受信するためのコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
using System.Net; using System.Net.Sockets; using System.Net.NetworkInformation; private Socket? mMulticastSock = null; private EndPoint? mLocalEp = null; public bool CreateMulticastSocket(String src_ip, int src_port, String bind_ip) { mMulticastSock = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); try { /* Socketオプション: 受信タイムアウト(ms) */ mMulticastSock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000); /* Socketオプション: Nagleアルゴリズム無効 */ mMulticastSock.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.NoDelay, true); /* Socketオプション: 使用済みアドレスにソケットのBindを許可 */ mMulticastSock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); /* ローカルエンドポイント */ mLocalEp = (EndPoint)new IPEndPoint(IPAddress.Parse(bind_ip), src_port); /* エンドポイントをバインド */ mMulticastSock.Bind(mLocalEp); /* 対応するNICを検索 */ NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); IPv4InterfaceProperties? prop = null; foreach (NetworkInterface adapter in nics) { /* Skip Loopback */ if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue; /* LINK-UP */ if (adapter.OperationalStatus != OperationalStatus.Up) continue; /* Multicast */ if (!adapter.SupportsMulticast) continue; /* IPv4 */ if (!adapter.Supports(NetworkInterfaceComponent.IPv4)) continue; /* Compare IP */ IPInterfaceProperties props = adapter.GetIPProperties(); foreach (IPAddressInformation addr in props.UnicastAddresses) { if (addr.Address.ToString().Equals(bind_ip)) { prop = props.GetIPv4Properties(); Console.WriteLine("Found {0} on Index={1}", bind_ip, prop.Index); break; } } if (prop != null) break; } if (prop == null) throw new Exception("Not Found NIC for Bind Address"); /* マルチキャストグループに参加 */ MulticastOption mcast_opt = new MulticastOption( IPAddress.Parse(src_ip), prop.Index); mMulticastSock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, mcast_opt); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.ToString()); mMulticastSock.Shutdown(SocketShutdown.Both); mMulticastSock.Close(); mMulticastSock = null; mLocalEp = null; return false; } return true; } |
呼び出し
CreateMulticastSocket("224.1.1.2", 5004, "192.168.56.2");
解説
28~31行目で、ローカルホストのIPアドレスを元に IPEndPoint を作成し、
Socket.Bind でバインドしていますが、これだけでは異なる NIC の方へ IGPM を投げてしまいます。
Multicast を受信したい NIC を指定したい場合は、AddMembership で指定する
MulticastOption を生成する際、NIC の Index を指定する必要があります。

おわりに
Bind してるんだから、気を利かせてくれよ。。。(愚痴)