14

Web サーバーからデータをログに記録する C# アプリケーションを開発しています。次の投稿リクエストを Web サーバーに送信し、応答を待ちます。

    /// <summary>
    /// Function for obtaining testCgi data 
    /// </summary>
    /// <param name="Parameters"></param>
    /// <returns></returns>
    private string HttpmyPost(string Parameters)
    {
        string str = "No response";
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriTestCGI);
        request.Method = "POST";

        byte[] bytes = Encoding.UTF8.GetBytes(Parameters);
        request.ContentLength = bytes.Length;

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(bytes, 0, bytes.Length);
        requestStream.Close();

            WebResponse response = request.GetResponse();
            Stream stream = response.GetResponseStream();
            StreamReader reader = new StreamReader(stream);

            try
            {
                var result = reader.ReadToEnd();
            stream.Dispose();
            str = result.ToString();
            reader.Dispose();
        }
        catch (WebException ex)
        {
            //System.Windows.Forms.MessageBox.Show(ex.Message);
            System.Diagnostics.Trace.WriteLine(ex.Message);

        }
        finally
        {
            request.Abort();
        }
        return str;
    }

エラーが発生しています

> "The underlying connection was closed: The connection was closed
> unexpectedly"

エラーをデバッグしようとしましたが、Firefox からの投稿要求を確認するためにフィドラーを使用しました。驚いたことに、Fiddler が私のプログラムであったときはいつでも、完全に機能していました。フィドラーを閉じると、同じエラーが発生します。

Fiddler はプロキシとして機能しているため、一部の設定が変更される可能性があると思われます。webclient を使用してみましたが、結果は同じでした。

Pythonでリクエストをコーディングしようとしたところ、問題なく正常に動作しました。もちろん、IronPython をインストールしてその特定の関数をラップするオプションもありますが、これはやり過ぎであり、エレガントさに欠けると考えているため、より無駄のないアプローチを追求しています。これは設定の調整にすぎないと思います。

私は変更を試みましたが、私の場合は無関心です。

request.Accept 
request.ReadWriteTimeout 
request.Timeout 
request.UserAgent 
request.Headers
request.AutomaticDecompression 
request.Referer
request.AllowAutoRedirect
//request.TransferEncoding 
request.Expect
request.ServicePoint.Expect100Continue 
request.PreAuthenticate 
request.KeepAlive 
request.ProtocolVersion 
request.ContentType

上記の調整の有無にかかわらず、コードは Fiddler がデータをキャプチャしているときに機能します。

また、プログラムが次の場所でエラーを生成することも注目に値するかもしれません

WebResponse response = request.GetResponse();

更新: @EricLaw の提案に従って、レイテンシーを調査しました。 Nagle アルゴリズムの有効化を提案する間隔を追加すると、この記事 HttpWebRequest が遅くなることがわかりました。現在、閉じられた接続はありませんが、全体的な応答にわずかな遅れがあります (非同期ではなく winform を使用する場合)。

4

3 に答える 3

30

Fiddler がどのように「魔法のように」物事を修正できるかについて少し書いています

発生している問題は、実際には .NET Framework 自体のバグです。HTTP のルールでは、サーバーは最初の応答を送信した後、いつでも KeepAlive 接続を閉じることができます (たとえば、クライアントが KeepAlive 動作を要求した場合でも、接続で別の要求を受け入れる必要はありません)。

Connection: close.NET には、応答の完了後にサーバーが接続を閉じる場合に、サーバーが応答ヘッダーを含めることを期待するというバグがあります。サーバーがConnection: Closeヘッダーなしで接続を閉じた場合 (RFC2616 では完全に有効)、.NET は接続で次の要求を送信しようとしたときに閉じられた接続に遭遇し、この例外をスローします。.NETが行うべきことは、サイレントに新しい接続を作成し、その新しい接続で要求を再送信することです。

Fiddler は、サーバーが接続を閉じても気にせず、クライアントへの接続を維持するため、この問題を解決します。クライアントが 2 番目の要求を送信すると、Fiddler はサーバーへの接続を再利用しようとし、接続が閉じられていることを認識して、サイレントに新しい接続を作成します。

コードでこの問題を軽減するには、次のようにします。

  1. リクエストでキープアライブを無効にする (これによりパフォーマンスが低下します)
  2. 例外をキャッチして自動的に再試行する
  3. サーバーを変更して接続をより長く維持する

アプローチ #3 は、サーバーを制御している場合にのみ機能します。クライアントは、使用後に接続を閉じるゲートウェイ/プロキシの背後にある可能性があるため、おそらくアプローチ #2 も使用する必要があります。

于 2014-01-31T18:30:43.897 に答える
0

提案と質問: 1) 実際に何が起こっているのかを確認したい場合は、Wireshark をインストールしてください。送受信されているものを正確に表示します。そしてそれはフィドラーと比較することを可能にします。request.ContentType = "...." のようなヘッダーが欠落していると思いますが、wireshark だけがどのヘッダーを表示するかを示します (HttpWebRequest によって送信されず、作業中の代替手段を介して送信されます)。

2) http 応答コンテンツ内でエラーが発生していますか、それとも例外ですか? 例外の場合は、catch でキャッチされていますか、それともリクエスト中に try ステートメントの前に発生していますか?

于 2014-01-31T14:16:22.230 に答える
-1

Fiddler はインターネット プロキシとして機能します。Fiddler の実行中に (そしておそらくブラウザーからも) コードが機能する場合は、プロキシ設定に問題がある可能性があります。

于 2014-01-31T14:26:19.173 に答える