将UDP消息广播到所有可用的网卡
我需要向特定的IP和端口发送UDP消息。
由于有3张网卡,
10.1.xx 10.2.xx 10.4.xx
当我发送UDP消息时,我只在一个网络适配器中收到消息…其余的ip没有收到。
我想在发送消息时检查网络适配器。 我怎样才能做到这一点?
目前我使用以下内容:
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(LocalIP), 0); IPEndPoint targetEndPoint = new IPEndPoint(TargetIP, iTargetPort); UdpClient sendUdpClient = new UdpClient(localEndPoint); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);
这实际上比听起来更棘手,因为如果你有多个接口,广播将不会总是出现在所有接口上。 为了解决这个问题,我创建了这个类。
public class MyUdpClient : UdpClient { public MyUdpClient() : base() { //Calls the protected Client property belonging to the UdpClient base class. Socket s = this.Client; //Uses the Socket returned by Client to set an option that is not available using UdpClient. s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1); } public MyUdpClient(IPEndPoint ipLocalEndPoint) : base(ipLocalEndPoint) { //Calls the protected Client property belonging to the UdpClient base class. Socket s = this.Client; //Uses the Socket returned by Client to set an option that is not available using UdpClient. s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1); } }
然后通过广播发送UDP数据包,我使用类似下面的内容。 我正在使用IPAddress.Broadcast
和MyUdpClient
,它与您的代码不同。
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(LocalIP), 0); IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort); MyUdpClient sendUdpClient = new MyUdpClient(localEndPoint); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);
此外,您应该注意,当您使用特定的ipaddress
而不是广播时,路由表仅将其发送到与该地址匹配的接口。
所以在你的例子中,使用了单播。 您需要将LocalIP
设置为要发送到的本地接口的IP。 使用三个接口,您将拥有三个本地IP,您需要选择要使用的正确IP。
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(LocalIP), 0); IPEndPoint targetEndPoint = new IPEndPoint(TargetIP, iTargetPort); MyUdpClient sendUdpClient = new MyUdpClient(localEndPoint); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);
由于路由已关闭,您可能会在所有接口上看到它,但您需要针对单播情况对其进行测试。
如果您不关心发送IP或端口,则可以使用以下代码。
IPEndPoint targetEndPoint = new IPEndPoint(TargetIP, iTargetPort); MyUdpClient sendUdpClient = new MyUdpClient(); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);
或用于广播
IPEndPoint targetEndPoint = new IPEndPoint(IPAddress.Broadcast, iTargetPort); MyUdpClient sendUdpClient = new MyUdpClient(); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);
IPAddress.Broadcast
的问题是它们不会通过任何网关路由。 要解决此问题,您可以创建IPAddresses
列表,然后循环并发送。 此外,由于发送可能因您无法控制的网络问题而失败,因此您还应该有一个try / catch块。
ArrayList ip_addr_acq = new ArrayList(); ip_addr_acq.Add(IPAddress.Parse("10.1.1.1")); // add to list of address to send to try { foreach (IPAddress curAdd in ip_addr_acq) { IPEndPoint targetEndPoint = new IPEndPoint(curAdd , iTargetPort); MyUdpClient sendUdpClient = new MyUdpClient(); int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint); Thread.Sleep(40); //small delay between each message } } catch { // handle any exceptions }
编辑:请参阅上面的更改为具有多个接口的单播,还有问题尝试将数据包单播到可用网络。
扩大雷克斯的答案。 这使您无需硬编码要广播的IP地址。 循环遍历所有接口,检查它们是否已启动,确保它具有IPv4信息,并且IPv4地址与之关联。 只需将“data”变量更改为您要广播的数据,将“目标”端口更改为您想要的数据。 小缺点是如果一个接口有多个与之关联的ip地址,它将从每个地址广播。 注意:这也将尝试通过任何VPN适配器发送广播(通过网络和共享中心/网络连接,Win 7+validation),如果您想接收响应,则必须保存所有客户端。 您也不需要二级课程。
foreach( NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces() ) { if( ni.OperationalStatus == OperationalStatus.Up && ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() != null ) { int id = ni.GetIPProperties().GetIPv4Properties().Index; if( NetworkInterface.LoopbackInterfaceIndex != id ) { foreach(UnicastIPAddressInformation uip in ni.GetIPProperties().UnicastAddresses ) { if( uip.Address.AddressFamily == AddressFamily.InterNetwork ) { IPEndPoint local = new IPEndPoint(uip.Address.Address, 0); UdpClient udpc = new UdpClient(local); udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1); byte[] data = new byte[10]{1,2,3,4,5,6,7,8,9,10}; IPEndPoint target = new IPEndPoint(IPAddress.Broadcast, 48888); udpc.Send(data,data.Length, target); } } } } }
如果您要发送到特定的IP地址,那么您是单播,而不是广播。
我通过从每个适配器发送UDP广播解决了这个问题(使用bind):
public static void SNCT_SendBroadcast(out List DevicesList) { DevicesList = new List (); byte[] data = new byte[2]; //broadcast data data[0] = 0x0A; data[1] = 0x60; IPEndPoint ip = new IPEndPoint(IPAddress.Broadcast, 45000); //braodcast IP address, and corresponding port NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); //get all network interfaces of the computer foreach (NetworkInterface adapter in nics) { // Only select interfaces that are Ethernet type and support IPv4 (important to minimize waiting time) if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet) { continue; } if (adapter.Supports(NetworkInterfaceComponent.IPv4) == false) { continue; } try { IPInterfaceProperties adapterProperties = adapter.GetIPProperties(); foreach (var ua in adapterProperties.UnicastAddresses) { if (ua.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { //SEND BROADCAST IN THE ADAPTER //1) Set the socket as UDP Client Socket bcSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //broadcast socket //2) Set socker options bcSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); bcSocket.ReceiveTimeout = 200; //receive timout 200ms //3) Bind to the current selected adapter IPEndPoint myLocalEndPoint = new IPEndPoint(ua.Address, 45000); bcSocket.Bind(myLocalEndPoint); //4) Send the broadcast data bcSocket.SendTo(data, ip); //RECEIVE BROADCAST IN THE ADAPTER int BUFFER_SIZE_ANSWER = 1024; byte[] bufferAnswer = new byte[BUFFER_SIZE_ANSWER]; do { try { bcSocket.Receive(bufferAnswer); DevicesList.Add(GetMyDevice(bufferAnswer)); //Corresponding functions to get the devices information. Depends on the application. } catch { break; } } while (bcSocket.ReceiveTimeout != 0); //fixed receive timeout for each adapter that supports our broadcast bcSocket.Close(); } } } catch { } } return; }