背景
C# のアプリ内で、netsh
を外部プロセスでコールしてIPアドレスを変更する処理をしていたのですが、プロセスを同期処理にして処理が返ってきても、IPアドレスを変更した瞬間は状態が「仮承諾」となっていて通信が不可になる現象が発生しました。
この「仮承諾」状態は、内部で5秒程度で「優先」に変わって、以降は通信が可能になるようです。
しかし、アプリ的に netsh
でIPアドレスを変更する処理が完了して、いつから通信開始していいのか判断する必要があるので「優先になったタイミングを知りたい」ということになりました。
ということで、首題の通り、IPアドレスの状態を取得する方法を調べたので、備忘録で残しておきます。
実装
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
namespace IpAddressWork.Core
{
static public class IPNetwork
{
/// <summary>
/// マルチキャストのIPアドレス(IPv4のみ)の状態を取得する
/// </summary>
/// <returns></returns>
public static IEnumerable<(IPAddress address, DuplicateAddressDetectionState state)> GetMulticastIPv4AddressInformationCollection()
{
foreach (var nif in GetEthernetIPv4Properties())
{
var multicastIPAddressInformationCollection = nif.MulticastAddresses;
foreach (var multicastIPAddressInformation in multicastIPAddressInformationCollection)
{
if (multicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
yield return (multicastIPAddressInformation.Address, multicastIPAddressInformation.DuplicateAddressDetectionState);
}
}
}
}
/// <summary>
/// ユニキャストのIPアドレス(IPv4のみ)の状態を取得する
/// </summary>
/// <returns></returns>
public static IEnumerable<(IPAddress address, DuplicateAddressDetectionState state)> GetUnicastIPv4AddressInformationCollection()
{
foreach (var nif in GetEthernetIPv4Properties())
{
var unicastIPAddressInformationCollection = nif.UnicastAddresses;
foreach (var unicastIPAddressInformation in unicastIPAddressInformationCollection)
{
if (unicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
yield return (unicastIPAddressInformation.Address, unicastIPAddressInformation.DuplicateAddressDetectionState);
}
}
}
}
static IEnumerable<IPInterfaceProperties> GetEthernetIPv4Properties()
{
return NetworkInterface.GetAllNetworkInterfaces()
// イーサネットでフィルター
.Where(i => i.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
.Select(i => i.GetIPProperties())
// ここでは IPv4 でフィルタリングしています。必要に応じて IPv6 にしたりしてください
.Where(e => e.GetIPv4Properties() != null);
}
}
}
上記の GetMulticastIPv4AddressInformationCollection
メソッドにて、IPアドレスと状態のタプルのリストが取得できます。
状態は DuplicateAddressDetectionState
で表されており、Tentative
が「仮承諾」、Preferred
が「優先」に該当します。
(詳細は公式を参照)
例えば以下のような感じで状態を監視して、「優先」状態になったことを判断できるようになりました。
//「仮承諾」状態だと通信が不可なので、「優先」になるまで待つ
await Observable.Interval(TimeSpan.FromMilliseconds(100)).Where(x =>
{
return GetUnicastIPv4AddressInformationCollection().All(i => i.state == DuplicateAddressDetectionState.Preferred);
})
.FirstAsync().ToTask();
参考
- PC上のネットワーク情報を列挙する方法はこちらを参考にさせて頂きました
コメント