「The remote name could not be resolved(リモート名を解決できません)」が C# の HttpClient で散発し、しかも数百回に1回だけ落ちる――この手の障害は、見た目が「タイムアウト」っぽくても、実際はHTTP より手前の DNS 名前解決(ホスト名→IP)の失敗であるケースが少なくありません。本記事では、WinForms デスクトップアプリで起きがちな状況を前提に、原因の整理、証明(ログ化)方法、アプリ側でできる現実的な対策までをまとめます。
現象の整理:「Timeout」と「名前解決失敗」は別物
まず大事なのは、例外メッセージが示している“失敗した場所”を切り分けることです。「The remote name could not be resolved」= DNS の名前解決に失敗を強く示唆します。これは「サーバに届いたが応答が遅い(Timeout)」とは、発生ポイントが違います。
| 見えるエラー/症状 | 失敗している段階 | よくある誤解 | まず見るべきポイント |
|---|---|---|---|
| An error occurred while sending the request. | 送信処理全般(内側に原因がある) | 全部タイムアウト扱いにしてしまう | InnerException を必ず確認 |
| The remote name could not be resolved: ‘public.api.XXXX.com’ | DNS 名前解決 | サーバが重い/落ちてる | DNS サーバ、VPN/プロキシ、回線品質 |
| TaskCanceledException / timeout | DNS/接続/応答待ちのどれかが長い | DNS は関係ないと思い込む | どこで詰まっているか(DNS/Connect/Read)をログ化 |
| 接続できない(Connection refused / reset) | TCP 接続以降 | DNS と混同 | IP までは引けているか、FW/ルーティング/サーバ側制限 |
ポイントは、HTTP のタイムアウト値(HttpClient.Timeout)を調整しても「名前解決失敗」そのものは治らないことがある点です。タイムアウトは「時間切れ」の制御であって、「DNS が失敗する」状態を根本的に変えるものではありません。
なぜ「200〜300リクエスト後に散発」しやすいのか
「最初はほぼ成功するのに、平均 200〜300 リクエスト後に散発的に失敗する」というパターンには、いくつか“現場でありがちな”背景があります。特に WinForms のようなデスクトップアプリで、短時間に連続アクセスする場合に起きやすいです。
- DNS サーバやネットワークが一瞬だけ不安定(社内DNS、VPN、Wi-Fi ローミング、回線瞬断など)
- DNS 問い合わせが増えすぎる(HttpClient/ハンドラを都度作る実装だと、名前解決が増えやすい)
- 端末側のリソース逼迫(ソケット枯渇、ハンドラ/接続管理の破綻が、別の症状として現れる)
- セキュリティ製品やプロキシが介在(DNS/HTTPS 検査、名前解決の遅延/失敗が“散発”しやすい)
- ドメイン運用(DNS ローテーション、低 TTL、CDN/ロードバランサ)が絡み、引けるIPが変わる
「DNS が悪い」と断定する前に、“どの DNS サーバで、いつ、何回失敗したか”を証拠として残すのが最短ルートです。ここができると、サーバ提供元/ネットワーク管理者への説明も一気に通ります。
最優先の切り分け:DNS を“継続監視”して証明する
散発障害は、1回だけの ping や nslookup では「たまたま成功」を引きがちです。必要なのは、問題が起きるタイミングを含めて連続的に観測し、ログとして残すことです。
継続監視でやること
- 同じ端末で、同じ時間帯に、対象ドメイン(public.api.XXXX.com)を一定間隔で引き続ける
- 成功/失敗だけでなく、どの DNS サーバに問い合わせたかも記録する
- 可能なら「社内 DNS」「8.8.8.8(Google)」「1.1.1.1(Cloudflare)」など、複数サーバに同時問い合わせして差を見る
PowerShell:Resolve-DnsName を一定間隔で回してログ化(おすすめ)
以下は WordPress にそのまま貼れるように、シンプルで運用しやすい形にした例です。成功時は解決した IP、失敗時は例外メッセージを CSV で残します。
$Domain = "public.api.XXXX.com"
$IntervalMs = 1000
$LogPath = ".\dns_monitor.csv"
# 自PCが使用しているDNSサーバ一覧を取得(IPv4を優先)
$DnsServers = (Get-DnsClientServerAddress -AddressFamily IPv4).ServerAddresses | Select-Object -Unique
# 比較用にパブリックDNSも追加(必要に応じて)
$DnsServers += @("8.8.8.8", "1.1.1.1") | Select-Object -Unique
if (!(Test-Path $LogPath)) {
"timestamp,domain,dnsServer,success,ips,error" | Out-File -Encoding UTF8 $LogPath
}
Write-Host "Monitoring DNS for $Domain ..."
Write-Host ("DNS Servers: " + ($DnsServers -join ", "))
while ($true) {
foreach ($server in $DnsServers) {
$ts = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss.fff")
try {
$res = Resolve-DnsName -Name $Domain -Type A -Server $server -ErrorAction Stop
$ips = ($res | Where-Object { $_.Type -eq "A" } | Select-Object -ExpandProperty IPAddress) -join "|"
"$ts,$Domain,$server,True,$ips," | Out-File -Append -Encoding UTF8 $LogPath
}
catch {
$msg = $_.Exception.Message.Replace(",", " ")
"$ts,$Domain,$server,False,,$msg" | Out-File -Append -Encoding UTF8 $LogPath
}
}
Start-Sleep -Milliseconds $IntervalMs
}
ログの見方はシンプルです。
- 社内DNSだけ失敗して、8.8.8.8/1.1.1.1 が安定 → 社内DNS/VPN/プロキシ/回線の疑いが濃い
- 全部で同時に失敗する瞬間がある → 端末側のネットワーク瞬断(Wi-Fi/有線/ドライバ/セキュリティ)や回線品質の疑い
- IP は取れるのに HttpClient だけ落ちる → DNS 以外(接続/証明書/プロキシ/ソケット枯渇)も並行調査
nslookup での継続監視(環境によってはこちらが通りやすい)
Resolve-DnsName が使えない/挙動が不安な環境向けに、nslookup でも同様の監視ができます。
$Domain = "public.api.XXXX.com"
$Server = "8.8.8.8"
$LogPath = ".\nslookup_monitor.log"
while ($true) {
$ts = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss.fff")
$out = nslookup $Domain $Server 2>&1
Add-Content -Encoding UTF8 $LogPath ("[" + $ts + "] " + $Domain + " via " + $Server)
Add-Content -Encoding UTF8 $LogPath ($out -join "`n")
Add-Content -Encoding UTF8 $LogPath ("")
Start-Sleep -Milliseconds 1000
}
DNS/ネットワーク側の「原因候補」と確認ポイント
「DNS が落ちている」と言っても、実際には経路・機器・運用が絡みます。現場で効くのは、原因を“候補”に落として、確認の順番を決めることです。
| 原因候補 | 起き方の特徴 | 確認コマンド/観測 | 対処の方向性 |
|---|---|---|---|
| 社内 DNS の不調/過負荷 | 数百回に1回、または特定時間帯だけ失敗 | Resolve-DnsName を社内DNS指定で継続監視 | DNS 冗長化、性能見直し、DNS サーバ変更 |
| VPN 切替/再接続 | VPN ON/OFF 付近で失敗が増える | 失敗時刻と VPN ログ、NIC 状態の相関 | VPN 設定/分割トンネル、DNS ルール調整 |
| プロキシ/セキュリティ製品の介在 | 特定 PC だけ/特定ネットワークだけ不安定 | 同一ドメインを別PC/別回線で比較 | 例外設定、製品ログ確認、SSL 検査設定の見直し |
| Wi-Fi ローミング/電波不良 | 移動中・席替え・電波強度変化で発生 | Wi-Fi のイベントログ、瞬断の有無 | 有線化、AP 切替、ドライバ更新 |
| DNS ローテーション/低 TTL | 解決される IP が頻繁に変わる | Resolve-DnsName ログの IP 変化 | アプリ側は接続の握りっぱなし回避(後述) |
合わせて、端末がどの DNS を使っているかは必ず押さえます。
ipconfig /all
Get-DnsClientServerAddress
ipconfig /displaydns
また、検証として短時間だけ DNS キャッシュをクリアして再現性が変わるかを見るのも有効です(恒久対策ではありません)。
ipconfig /flushdns
アプリ側でできる改善:「DNS問題」に見えても効く実装の見直し
DNS が原因っぽいときでも、アプリ側の実装で失敗率が上がっているケースがあります。特に重要なのがHttpClient の作り方です。
やりがち:毎回 using(new HttpClient()) で作り直す
1リクエストごとに HttpClient を生成・破棄すると、環境によっては以下が起きやすくなります。
- 接続管理が不安定になりやすい(TIME_WAIT の増加など)
- 名前解決や接続の初期化が増え、DNS/ネットワークに負荷が寄る
- 散発障害が“より表に出やすい”
推奨:IHttpClientFactory を使う(.NET 6+ / .NET Core)
WinForms でも DI を持ち込めます。HttpClient の使い捨てを避けつつ、ハンドラを適切な周期で更新できるのが強みです。
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public static class HttpClientSetup
{
public static ServiceProvider BuildProvider()
{
var services = new ServiceCollection();
services.AddHttpClient("PublicApi", client =>
{
client.BaseAddress = new Uri("https://public.api.XXXX.com/");
client.Timeout = TimeSpan.FromSeconds(30);
})
// DNS変更やバックエンド切替がある環境では、接続を永遠に握り続けない設計が有利
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2),
// 必要なら ConnectTimeout なども検討(環境/要件次第)
});
return services.BuildServiceProvider();
}
public static async Task DemoAsync(ServiceProvider provider)
{
var factory = provider.GetRequiredService<IHttpClientFactory>();
var http = factory.CreateClient("PublicApi");
var res = await http.GetAsync("v1/health");
res.EnsureSuccessStatusCode();
}
}
ポイントは、HttpClient は“使い回す”が、接続(ハンドラ)は“適度に更新する”というバランスです。DNS ローテーションやバックエンド切替がある API ほど、この設計が効いてきます。
.NET Framework(WinForms)で IHttpClientFactory を使わない場合の現実解
.NET Framework の WinForms だと、環境や制約で IHttpClientFactory を導入しにくいこともあります。その場合でも、次の方針が現実的です。
| 方針 | メリット | 注意点 | 向いているケース |
|---|---|---|---|
| HttpClient をアプリ全体で1つ(シングルトン) | ソケット枯渇を避けやすい、実装が簡単 | DNS/接続変更がある環境では“握りっぱなし”リスク | 固定のバックエンド、社内ネットで安定 |
| HttpClient を一定周期で作り直す | DNS 変更/経路変更への追従がしやすい | 作り直し頻度が高すぎると逆効果 | CDN/負荷分散があり IP が変わりやすい |
| ServicePoint の設定(.NET Framework) | 接続のリース/更新で“古い接続”を避けられる | グローバル影響が出る、慎重に | Framework 固有の制御が必要 |
例えば、.NET Framework で「使い回しはするが、定期的に接続を更新」したいときは、ServicePoint 設定が候補になります(対象ホストに限定して使うのが無難です)。
using System;
using System.Net;
using System.Net.Http;
public static class FrameworkTuning
{
private static readonly Uri ApiUri = new Uri("https://public.api.XXXX.com/");
public static HttpClient Create()
{
// 例:DNS の再評価や接続の更新を促す(環境により調整)
ServicePointManager.DnsRefreshTimeout = (int)TimeSpan.FromMinutes(1).TotalMilliseconds;
var sp = ServicePointManager.FindServicePoint(ApiUri);
sp.ConnectionLeaseTimeout = (int)TimeSpan.FromMinutes(5).TotalMilliseconds;
ServicePointManager.DefaultConnectionLimit = 256;
var handler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
var client = new HttpClient(handler)
{
BaseAddress = ApiUri,
Timeout = TimeSpan.FromSeconds(30)
};
return client;
}
}
ただし、ここで強調しておきたいのは、これらは“DNS が落ちる根本原因”を直す魔法ではないという点です。あくまで「アプリ側の作りで、問題を悪化させない」「DNS ローテーション等の環境変化に強くする」ための手当です。
リトライ(指数バックオフ)で“散発”を吸収する
DNS やネットワークの一瞬の不安定さは、ゼロにできないことがあります。そこで実運用では、一時的な失敗を前提にして、落ちにくくするのが定石です。
リトライ設計のコツ
- 回数は有限(例:3〜5回)にする
- 指数バックオフ(待ち時間を 200ms→500ms→1s→2s… のように増やす)
- ジッター(ランダム揺らぎ)を入れて同時再試行を避ける
- 「名前解決失敗」は多くの場合短時間で復帰する可能性があるが、長引くなら環境問題として切り分ける
| 項目 | おすすめ | 理由 |
|---|---|---|
| 最大リトライ回数 | 3〜5回 | 短い瞬断を吸収しつつ、無限ループを避ける |
| 初期待機 | 200〜500ms | DNS の一時的な揺らぎに素早く追従 |
| 増加方式 | 指数(×2) | 長引く障害では負荷を増やさない |
| ジッター | ±0〜200ms 程度 | 多数クライアントの同時再試行を避ける |
Polly を使う場合は、「HttpRequestException の内側に SocketException があり、メッセージが名前解決系」などを条件に入れると、不要な再試行(例えば 4xx)を避けられます。逆に、リトライが効くのは“瞬間的な失敗”に限るので、同時にDNS 継続監視ログで根本原因を詰めるのが重要です。
「サーバが悪いのか?」を説明できる形にする(証拠の作り方)
今回の例外は、HTTP 応答が返ってこない以前に、ホスト名を IP に解決できないことを示します。つまり「サーバアプリが遅い/落ちている」よりも、次のどちらかで説明がつきやすいです。
- クライアント側(端末/ネットワーク/社内DNS/VPN/プロキシ)で名前解決が失敗している
- ドメイン運用(DNS サーバ群、権威DNS、CDN など)のどこかで一部の問い合わせが落ちている
サーバ提供元へ問い合わせる場合も、単に「落ちた」ではなく、次の情報があると会話が速くなります。
| ログ項目 | 例 | なぜ重要か |
|---|---|---|
| 発生時刻(ミリ秒まで) | 2025-12-28 10:12:34.567 | DNS/ネットワークのイベントと突合できる |
| 対象ドメイン | public.api.XXXX.com | 同一ドメインでも地域・経路で差が出る |
| 問い合わせた DNS サーバ | 10.0.0.53 / 8.8.8.8 | 社内DNS起因か、外部起因か切り分けできる |
| Resolve-DnsName の結果 | 成功時IP / 失敗時メッセージ | DNS 失敗そのものの証明になる |
| HttpClient 例外の InnerException | SocketException など | “どの段階で落ちたか”を確定できる |
また、「IP を直打ちすれば回避できるのでは?」と考えがちですが、HTTPS では SNI や証明書検証の都合でうまくいかないことが多く、運用回避策としてはおすすめしません。原因が DNS にあるなら、DNS を安定させる(または利用DNSを変更する)のが筋です。
WinForms で起きやすい落とし穴:同期ブロックと大量並列
DNS とは別軸ですが、WinForms で連続リクエストを回すときは、次も失敗率を押し上げることがあります。
- .Result / .Wait() で UI スレッドをブロックし、待ちが詰まる
- 短時間に大量並列を投げ、DNS/接続/プロキシに負荷を集中させる
可能なら async/await を基本にして、並列度も制御(SemaphoreSlim など)し、ネットワークに“呼吸”をさせると安定します。
private static readonly SemaphoreSlim _gate = new SemaphoreSlim(8); // 並列度を制限
public async Task<string> CallApiAsync(HttpClient http, string path)
{
await _gate.WaitAsync();
try
{
using var res = await http.GetAsync(path).ConfigureAwait(false);
res.EnsureSuccessStatusCode();
return await res.Content.ReadAsStringAsync().ConfigureAwait(false);
}
finally
{
_gate.Release();
}
}
PowerShell スクリプトが実行できないときの運用手順
継続監視スクリプトを回そうとして詰まりやすいポイントも、実務では頻出です。最低限ここだけ押さえるとスムーズです。
- スクリプトは .ps1 で保存する
- PowerShell を開き、スクリプトのあるフォルダへ移動して .\ファイル名.ps1 で実行する
- 実行ポリシーで止まる場合は、必要最小限の範囲(CurrentUser)で許可する
# 例:現在ユーザーだけ許可(管理者不要で済むことが多い)
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
社内ルールがある環境では、ポリシー変更が難しい場合もあります。その場合は、ネットワーク管理者に「継続監視のために必要」と目的を明確にして相談するのが近道です。
最終チェックリスト:再現・証明・改善を一気通貫で進める
- 例外は「An error occurred…」だけで判断せず、InnerException とメッセージを必ず記録する
- public.api.XXXX.com を Resolve-DnsName で継続監視し、失敗の瞬間をログに残す
- 社内DNS指定と 8.8.8.8/1.1.1.1 を比較し、どこで落ちているか切り分ける
- HttpClient を毎回作り直しているなら、IHttpClientFactory または “使い回し+適度な更新” に変更する
- 散発に備えて、有限回のリトライ(指数バックオフ+ジッター)を実装する
- VPN/プロキシ/セキュリティ製品が絡むなら、同一ドメインを別回線・別PCでも比較する
「リモート名を解決できません」は、原因がサーバアプリ側とは限らないぶん、放置すると“たまに落ちる謎バグ”として長期化しがちです。逆に言えば、DNS 継続監視と実装の見直しをセットで進めれば、短期間で“再現性のある説明”と“落ちにくい実装”に持ち込みやすい障害でもあります。

コメント