8

Web サーバーに接続してデフォルトのホームページを取得する単純な HTTP クライアントを実装しています。ここにあり、うまく機能します:

using System;
using System.Net.Sockets;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpClient tc = new TcpClient();
            tc.Connect("www.google.com", 80);

            using (NetworkStream ns = tc.GetStream())
            {
                System.IO.StreamWriter sw = new System.IO.StreamWriter(ns);
                System.IO.StreamReader sr = new System.IO.StreamReader(ns);

                string req = "";
                req += "GET / HTTP/1.0\r\n";
                req += "Host: www.google.com\r\n";
                req += "\r\n";

                sw.Write(req);
                sw.Flush();

                Console.WriteLine("[reading...]");
                Console.WriteLine(sr.ReadToEnd());
            }
            tc.Close();
            Console.WriteLine("[done!]");
            Console.ReadKey();
        }
    }
}

上記のコードから以下の行を削除すると、プログラムはsr.ReadToEndでブロックされます。

req += "Host: www.google.com\r\n";

sr.ReadToEndsr.Readに置き換えましたが、何も読み取れません。Wireshark を使用して、何が起こっているかを確認しました。

Wireshark を使用してキャプチャされたパケットのスクリーンショット http://www.imagechicken.com/uploads/1252514718052893500.jpg

ご覧のとおり、GET リクエストの後、Google は応答せず、リクエストは何度も再送信されます。HTTP リクエストでHost部分を指定する必要があるようです。奇妙な部分は、WE DON'T です。telnetを使用してこのリクエストを送信すると、Google から応答がありました。telnet から送信されたリクエストもキャプチャしましたが、それは私のリクエストとまったく同じでした。

他の多くの Web サイト (Yahoo、Microsoft など) も試しましたが、結果は同じです。

では、telnet の遅延によって Web サーバーの動作が異なるのでしょうか (telnetでは、文字を 1 つのパケットにまとめて送信するのではなく、実際に入力するため)。


別の奇妙な問題は、HTTP/1.0HTTP/1.1に変更すると、プログラムが常にsr.ReadToEnd行でブロックされることです。これは、Web サーバーが接続を閉じていないためだと思います。

1 つの解決策は、Read (またはReadLine ) とns.DataAvailableを使用して応答を読み取ることです。しかし、すべての回答を読んだかどうかはわかりません。応答を読み取って、HTTP/1.1 要求の応答にバイトが残っていないことを確認するにはどうすればよいですか?


注: W3 が言うように、

Host request-header フィールドは、すべての HTTP/1.1 リクエストに付随する必要があります

(そして、HTTP / 1.1リクエストに対してそれを行いました)。しかし、 HTTP/1.0ではそのようなことは見たことがありません。また、telnet を使用してHostヘッダーなしでリクエストを送信しても、問題なく動作します。


アップデート:

TCP セグメントでプッシュフラグが 1 に設定されています。TCP/IP スタックをリセットするためにnetsh winsock resetも試しました。テスト コンピューターには、ファイアウォールもウイルス対策もありません。別のコンピュータにインストールされた Wireshark がパケットをキャプチャできるため、パケットは実際に送信されます。

また、他のリクエストもいくつか試しました。例えば、

string req = "";
req += "GET / HTTP/1.0\r\n";
req += "s df slkjfd sdf/ s/fd \\sdf/\\\\dsfdsf \r\n";
req += "qwretyuiopasdfghjkl\r\n";
req += "Host: www.google.com\r\n";
req += "\r\n";

すべての種類のリクエストで、Host:の部分を省略した場合、Web サーバーは応答しません。また、 Host : の部分を省略した場合、無効なリクエスト (上記のリクエストと同様) でさえ応答されます (400: HTTP によって)。要求の形式が正しくありません)。

nosHost:の部分は彼のマシンでは必要ないと言っており、これが状況をより奇妙にしています。

4

5 に答える 5

3

これは、TcpClient の使用に関連しています。

この投稿が古いことは知っています。他の誰かがこれに出くわした場合に備えて、この情報を提供しています。この回答は、上記のすべての回答の補足と考えてください。

一部のサーバーでは、IP アドレスごとに複数のドメインをホストするように設定されているため、HTTP ホスト ヘッダーが必要です。原則として、常に Host ヘッダーを送信します。良いサーバーは「Not Found」で応答します。一部のサーバーはまったく応答しません。

ストリームからデータを読み取る呼び出しがブロックされる場合、通常は、サーバーがさらにデータが送信されるのを待っているためです。これは通常、HTTP 1.1 仕様に厳密に従っていない場合に発生します。これを実証するために、最後の CR LF シーケンスを省略してから、ストリームからデータを読み取ってみてください。read の呼び出しは、クライアントがタイムアウトするか、サーバーが接続を終了して待機をあきらめるまで待機します。

これが少し光を当てることを願っています...

于 2012-06-13T15:51:16.603 に答える
2

その中で1つの質問を見つけました:

HTTP/1.1 リクエストのすべての応答を確実に読み取るにはどうすればよいですか?

そして、それは私が答えることができる質問です!

ここで使用しているすべてのメソッドは同期的です。これは使いやすいですが、信頼性はわずかでもありません。かなりの反応があり、その一部しか得られないとすぐに問題が発生します。

TcpClient 接続を最も確実に実装するには、すべての非同期メソッドとコールバックを使用する必要があります。関連するメソッドは次のとおりです。

1) TcpClient.EndConnect(...) を呼び出すコールバックで TcpClient.BeginConnect(...) との接続を作成します。
2) TcpClient.GetStream().BeginWrite(...) で TcpClient を呼び出すコールバックでリクエストを送信します。 GetStream().EndWrite(...)
3) TcpClient.GetStream().BeginRead(...) で応答を受け取り、TcpClient.GetStream().EndRead(...) を呼び出すコールバックを使用して、結果をStringBuilder バッファを呼び出し、次に TcpClient.GetStream().BeginRead(...) を (同じコールバックで) 0 バイトの応答を受信するまで再度呼び出します。

応答、応答全体、および応答のみを取得するという問題を解決するのは、最後のステップ (0 バイトが読み取られるまで BeginRead を繰り返し呼び出す) です。TCPを助けてください。

それが役立つことを願っています!

于 2010-06-23T17:22:33.423 に答える
0

ReadToEnd は、接続が閉じられるまで待機すると思います。ただし、閉じていないようです。代わりに継続的に読む必要があります。その後、期待どおりに動作します。

//Console.WriteLine(sr.ReadToEnd());
var bufout = new byte[1024];
int readlen=0;
do
{
    readlen = ns.Read(bufout, 0, bufout.Length);
    Console.Write(System.Text.Encoding.UTF8.GetString(bufout, 0, readlen));
} while (readlen != 0);
于 2012-07-11T21:13:55.570 に答える
0

Apache HTTPD や IIS など、独自のローカル マシンにインストールされた、十分にテストされ、広く受け入れられている標準の Web サーバーに対してコードを試すことをお勧めします。

Hostヘッダーなしで応答するように Web サーバーを構成し(IIS の既定の Web アプリケーションなど)、すべてがうまくいくかどうかを確認します。

要するに、Google、Yahoo などの Web サイトや Web アプリケーションを制御できないため、舞台裏で何が起こっているのかを実際に知ることはできません。
たとえば、Web サイトの管理者は、 HTTP プロトコルを使用する、ポート 80 での着信 TCP 接続用のデフォルト アプリケーションはありません。
しかし、TELNET プロトコルを使用して TCP ポート 23 経由で接続する場合、デフォルトの telnet アプリケーションを構成したい場合があります。

于 2010-06-24T08:05:04.680 に答える
-2

System.Net.Sockets.TcpClientの代わりにSystem.Net.WebClientを直接使用してみてください。

using System;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            WebClient wc = new WebClient();
            Console.WriteLine("[requesting...]");
            Console.WriteLine(wc.DownloadString("http://www.google.com"));
            Console.WriteLine("[done!]");
            Console.ReadKey();
        }
    }
}
于 2009-09-09T23:28:02.220 に答える