目的
Windows共有フォルダ上のファイルやディレクトリの有無を確認するときに、System.IO.File.ExistsやSystem.IO.Directory.Existsを使うと、対象のサーバーに繋がらない際に数秒から数十秒、長い場合数分待たされる場合があります。
今回は指定した秒数以内に接続できない場合 false を返すようなタイムアウト付きの有無確認を実装します。
条件
- Windowsファイル共有 (SMB/CIFS)
- .NET 5.0(.NET Framework 4.6以上でもおそらく動作)
対象
- 共有フォルダを持つサーバーの死活を手っ取り早く確認したい人(PINGが使えないケースなど)
- 死活がわからない共有フォルダ上のファイルの有無を手っ取り早く確認したい人
- とりあえず当該動作を探していた面倒くさがりの初心者
実装
細かな例外処理等は省略。コードの美しさも省略。
using System.Threading;
using System.Threading.Tasks;
using System.IO;
public class TimeoutIOUtils
{
//タイムアウト秒
public static int TimeoutSeconds{ get; set; } = 3;
//ファイル有無
public static bool FileExists(string path)
{
return TimeoutCore(() => File.Exists(path));
}
//ディレクトリ有無
public static bool DirectoryExists(string path)
{
return TimeoutCore(() => Directory.Exists(path));
}
//タイムアウト処理部分
private static bool TimeoutCore(Func<bool> existFunction)
{
try
{
var source = new CancellationTokenSource();
source.CancelAfter(TimeoutSeconds * 1000);
var task = Task.Factory.StartNew(() => existFunction(), source.Token);
task.Wait(source.Token);
return task.Result;
}
catch (OperationCanceledException ex)
{
return false;
}
catch (Exception ex)
{
throw;
}
}
}
追記: @albireo さんのコメントより改良案取り込み版
//タイムアウト処理部分
private static bool TimeoutCore(Func<bool> existFunction)
{
var task = Task.Factory.StartNew(() => existFunction());
return task.Wait(TimeoutSeconds * 1000) && task.Result;
}
使い方
private void Sample()
{
TimeoutIOUtils.TimeoutSeconds = 2; //タイムアウトを2秒に設定
var dir = @"\\192.168.0.XXX\example";
var file = @"\\192.168.0.XXX\example\example.txt";
//ディレクトリの有無確認
if(TimeoutIOUtils.DirectoryExists(dir))
{
Console.WriteLine("ディレクトリあります");
}
else
{
Console.WriteLine("ディレクトリないです");
}
//ファイルの有無確認
if(TimeoutIOUtils.FileExists(file))
{
Console.WriteLine("ファイルあります");
}
else
{
Console.WriteLine("ファイルないです");
}
}
補足
通常のFile.Existsなどと比べ若干のオーバーヘッド増加はあるはず。
Taskの使えない.NET Framework4.0とかだとThreadで似たようなことができます。
上記でとりあえず期待通り動作はしますが、Taskの扱いは筆者も初心者なので後処理含め正しくはないかもしれません。
各々調べて適当に改良してご利用下さい。