HttpClientでThe remote name could not be resolvedが散発する原因と対策|DNS名前解決の切り分け・ログ化・実装改善

「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 / timeoutDNS/接続/応答待ちのどれかが長い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〜500msDNS の一時的な揺らぎに素早く追従
増加方式指数(×2)長引く障害では負荷を増やさない
ジッター±0〜200ms 程度多数クライアントの同時再試行を避ける

Polly を使う場合は、「HttpRequestException の内側に SocketException があり、メッセージが名前解決系」などを条件に入れると、不要な再試行(例えば 4xx)を避けられます。逆に、リトライが効くのは“瞬間的な失敗”に限るので、同時にDNS 継続監視ログで根本原因を詰めるのが重要です。

「サーバが悪いのか?」を説明できる形にする(証拠の作り方)

今回の例外は、HTTP 応答が返ってこない以前に、ホスト名を IP に解決できないことを示します。つまり「サーバアプリが遅い/落ちている」よりも、次のどちらかで説明がつきやすいです。

  • クライアント側(端末/ネットワーク/社内DNS/VPN/プロキシ)で名前解決が失敗している
  • ドメイン運用(DNS サーバ群、権威DNS、CDN など)のどこかで一部の問い合わせが落ちている

サーバ提供元へ問い合わせる場合も、単に「落ちた」ではなく、次の情報があると会話が速くなります。

ログ項目例なぜ重要か
発生時刻(ミリ秒まで)2025-12-28 10:12:34.567DNS/ネットワークのイベントと突合できる
対象ドメインpublic.api.XXXX.com同一ドメインでも地域・経路で差が出る
問い合わせた DNS サーバ10.0.0.53 / 8.8.8.8社内DNS起因か、外部起因か切り分けできる
Resolve-DnsName の結果成功時IP / 失敗時メッセージDNS 失敗そのものの証明になる
HttpClient 例外の InnerExceptionSocketException など“どの段階で落ちたか”を確定できる

また、「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 継続監視と実装の見直しをセットで進めれば、短期間で“再現性のある説明”と“落ちにくい実装”に持ち込みやすい障害でもあります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次