C#でUDPブロードキャストを使用してネットワーク検出を行いたい。これを行う方法がわかりません。方法を教えてください。
このようにしたい tutorial 。
C#で同じことをするのは非常に簡単です
サーバ:
var Server = new UdpClient(8888);
var ResponseData = Encoding.ASCII.GetBytes("SomeResponseData");
while (true)
{
var ClientEp = new IPEndPoint(IPAddress.Any, 0);
var ClientRequestData = Server.Receive(ref ClientEp);
var ClientRequest = Encoding.ASCII.GetString(ClientRequestData);
Console.WriteLine("Recived {0} from {1}, sending response", ClientRequest, ClientEp.Address.ToString());
Server.Send(ResponseData, ResponseData.Length, ClientEp);
}
クライアント:
var Client = new UdpClient();
var RequestData = Encoding.ASCII.GetBytes("SomeRequestData");
var ServerEp = new IPEndPoint(IPAddress.Any, 0);
Client.EnableBroadcast = true;
Client.Send(RequestData, RequestData.Length, new IPEndPoint(IPAddress.Broadcast, 8888));
var ServerResponseData = Client.Receive(ref ServerEp);
var ServerResponse = Encoding.ASCII.GetString(ServerResponseData);
Console.WriteLine("Recived {0} from {1}", ServerResponse, ServerEp.Address.ToString());
Client.Close();
サーバーレスの別のソリューションを次に示します。私は、ラズベリーパイの束にネットワーク上でお互いを認識させる必要がありましたが、誰がアクティブになるかについての保証はありませんでした。したがって、このアプローチにより、全員がクライアントになることができます!完全なライブラリはGitHub(免責事項:私が作成しました)で利用でき、UWPアプリにとってこのプロセス全体が本当に簡単になります。
https://github.com/mattwood2855/WindowsIotDiscovery
このソリューションでは、デバイス名が一意であり、通信プロトコルとしてJSON文字列を使用することを想定していますが、他の形式を簡単に送信できます。また、実際にはすべてを試してみてください;)
一般的なメカニズム:
IpAdressを発見する
public string IpAddress
{
get
{
var hosts = NetworkInformation.GetHostNames();
foreach (var Host in hosts)
{
if (Host.Type == HostNameType.Ipv4) return Host.DisplayName;
}
return "";
}
}
リスナーを設定します
var udpPort = "1234";
var socket = new DatagramSocket();
socket.MessageReceived += ReceivedDiscoveryMessage;
await socket.BindServiceNameAsync(udpPort);`
着信データの処理
async void ReceivedDiscoveryMessage(DatagramSocket socket, DatagramSocketMessageReceivedEventArgs args)
{
// Get the data from the packet
var result = args.GetDataStream();
var resultStream = result.AsStreamForRead();
using (var reader = new StreamReader(resultStream))
{
// Load the raw data into a response object
var potentialRequestString = await reader.ReadToEndAsync();
// Ignore messages from yourself
if (args.RemoteAddress.DisplayName == IpAddress) return;
// Get the message
JObject jRequest = JObject.Parse(potentialRequestString);
// Do stuff with the data
}
}
メッセージを送信する
public async void SendDataMessage(string discoveryMessage)
{
// Get an output stream to all IPs on the given port
using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), udpPort))
{
// Get a data writing stream
using (var writer = new DataWriter(stream))
{
// Write the string to the stream
writer.WriteString(discoveryMessage);
// Commit
await writer.StoreAsync();
}
}
}
考えは、IPアドレスと名前を含む発見メッセージを送信することです。次に、メッセージ受信機能で、ip-nameのペアをデバイスのリストに追加します。 IPが特定の名前で変更された場合、重複を避けるための小さなロジックを追加し、IPアドレスを更新します。
ボーナスとして、各デバイスが知っているデバイスのリストを送信することができます。これにより、送信者があなたを認識しているときに応答しないことで、udpトラフィックを最小限に抑えることができます。受信者がリストを自分のリストと比較して、他のデバイスを検出することもできます。
冗長性はUDPの味方であり、パケットが配信されるという保証はありません。
同じ質問がありましたが、@ rufanovが示唆する答えほど簡単ではありませんでした。
ここで私が持っていたいくつかの状況:
いくつかの研究と作業の後、私はこの解決策に到達しました。このコードはserver側に対応し、ブラッドキャストメッセージに応答するすべてのデバイスのネットワーク検出を行います。
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;
}
私はそれが古いことを知っていますが、誰かがまだこれを必要とするかもしれません...受け入れられた答えは素晴らしいですが、サーバー側のこの小さな調整でさらに良いです。
Ilya Suzdalnitskiコメントの修正(2番目のClient.Receive呼び出しでロックアップ):
var responseData = Encoding.ASCII.GetBytes("someData");
while (true)
{
var server = new UdpClient(8888);
var clientEp = new IPEndPoint(IPAddress.Any, 0);
var clientRequestData = server.Receive(ref clientEp);
var clientRequest = Encoding.ASCII.GetString(clientRequestData);
Console.WriteLine($"Recived {clientRequest} from {clientEp.Address}, sending
response: {responseData}");
server.Send(responseData, responseData.Length, clientEp);
server.Close();
}
各応答の後にサーバーが閉じられ、再作成されるため、サーバーはロックせずに無限に動作する可能性があります。