向所有可用的网卡广播 UDP 消息
Posted
技术标签:
【中文标题】向所有可用的网卡广播 UDP 消息【英文标题】:Broadcasting UDP message to all the available network cards 【发布时间】:2010-11-08 22:18:52 【问题描述】:我需要向特定的 IP 和端口发送 UDP 消息。
由于有3个网卡,
10.1.x.x
10.2.x.x
10.4.x.x
当我发送 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);
【问题讨论】:
您能否进一步说明您想要尝试做的事情。 我的应用程序将向 10 多个系统中的应用程序发送消息。其中所有系统都在三个不同的网卡中。像 10.1.x.x / 10.2.x.x / 10.4.x.x 我只能在一个网卡 10.1.x.x 中接收消息,但不能在其他两个网卡中接收消息。所以我想检查网卡的可用性,然后发送消息。谢谢。 消息是否只是为了检查网络可用性,还是有其他一些有效负载/含义? 如果你想获得冗余,对于发送连接到网络 A,但接收器与 A 断开但连接到 B 和 C(由于网卡/电缆故障)的情况,并且您希望您的消息到达接收器,然后像 Rex 所说的那样发送它应该可以解决问题,但是您必须管理重复,并且可能在接收端进行乱序处理。 如果你只是想测试所有网络的每个主机的连通性,发送一个单播消息到每个网络A、B和C上的主机的网络地址。 【参考方案1】:这实际上比听起来要棘手,因为如果您有多个接口,则广播不会总是发送到所有接口。为了解决这个问题,我创建了这个类。
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,您需要选择正确的一个来使用。
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
编辑:请参阅上面更改为具有多个接口的单播以及Problem Trying to unicast packets to available networks.
【讨论】:
嗨 Rex Logan,当我尝试你的代码时,我收到以下错误:尝试对无法访问的主机进行套接字操作。我在这里感到困惑,具有该IP的网络适配器存在。当我调试以下行时... int numBytesSent = sendUdpClient.Send(CombineHeaderBody, CombineHeaderBody.Length, targetEndPoint);我看到了 targetEndPoint = 10.4.1.2:1982。当 targetEndPoint 是 10.1.1.1:1982 我在远程机器上接收数据包。 :( 如果您发送到 IPAddress.Broadcast 会发生什么?你也可以试试我添加的简化版本。 您也可以尝试注释掉 s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontRoute, 1);或 s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.Broadcast, 1) 看看会发生什么 它是一样的。能找到那个IP。我尝试 ping 10.4.1.2 但它显示“目标主机无法访问”。那么现在是网络问题还是应用问题?谢谢。 当我做 ipaddress.broadcast 时,我没有在远程机器上接收数据包。【参考方案2】:我通过从每个适配器发送 UDP 广播(使用绑定)解决了这个问题:
public static void SNCT_SendBroadcast(out List<MyDevice> DevicesList)
DevicesList = new List<MyDevice>();
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;
【讨论】:
稍作修改,这很适合我的需要,谢谢! 1) 您需要在 ForEach UnicastAddr 内移动外部 Try/Catch 块,并添加一个 finally 块来处理 bcSocket obj,因为在某些情况下绑定会抛出。 2) 仅在 NetworkInterfaceType.Ethernet 上进行限定会忽略一些常见的硬件,如无线网卡/等。 (NetworkInterfaceType.Wireless80211)【参考方案3】:雷克斯答案的扩展。这使您不必对要广播的 IP 地址进行硬编码。循环遍历所有接口,检查它们是否启动,确保它具有 IPv4 信息,并且 IPv4 地址与之关联。只需将“数据”变量更改为您想要广播的任何数据,并将“目标”端口更改为您想要的。一个小缺点是,如果一个接口有多个与之关联的 IP 地址,它将从每个地址广播出去。注意:这也将尝试通过任何 *** 适配器发送广播(通过网络和共享中心/网络连接,Win 7+ 已验证),如果您想接收响应,则必须保存所有客户端。您也不需要中学课程。
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);
【讨论】:
【参考方案4】:如果您要发送到特定的 IP 地址,那么您是单播,而不是广播。
【讨论】:
哦,是的,没错。但是我在发送之前选择网络吗? 啊,您试图用单个网卡将每个本地网卡的数据包发送到远程主机?如果您尝试测试每个网卡,请在该网络上找到一个主机以向其发送消息。让操作系统为你做路由。 但是操作系统总是路由到第一个网卡10.1.x.x 如果你想将不同的网卡发送到不同的主机地址。也就是说,如果它们都在三个网络上。它归结为您尝试实现的目标、测试网络连接或发送实际的更高层消息。以上是关于向所有可用的网卡广播 UDP 消息的主要内容,如果未能解决你的问题,请参考以下文章