Трансляция UDP-сообщения на все доступные сетевые карты
Мне нужно отправить UDP-сообщение на определенный IP-адрес и порт.
Так как есть 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);
4 ответа
Это на самом деле сложнее, чем кажется, потому что если у вас более одного интерфейса, трансляции не всегда будут выходить на все интерфейсы. Чтобы обойти это, я создал этот класс.
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
}
Изменить: см. Выше изменение одноадресной передачи с несколькими интерфейсами, а также проблема Попытка одноадресной передачи пакетов в доступные сети.
Я решил эту проблему, отправив UDP-трансляцию с каждого адаптера (используя bind):
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;
}
Расширение Ответа Рекса. Это позволяет вам не вводить жесткий код IP-адресов, по которым вы хотите транслировать. Проходит через все интерфейсы, проверяет их работоспособность, проверяет, есть ли у него информация IPv4 и с ним связан адрес IPv4. Просто измените переменную "data" на любые данные, которые вы хотите транслировать, и "target" порт на тот, который вы хотите. Небольшой недостаток заключается в том, что если с интерфейсом связано несколько IP-адресов, он будет транслироваться с каждого адреса. Примечание: это также будет пытаться отправлять широковещательные сообщения через любой адаптер VPN (через Центр управления сетями и общим доступом / Сетевые подключения, 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);
}
}
}
}
}
Если вы отправляете на определенный IP-адрес, то вы не используете широковещательную рассылку.