1

これは私がまだ解決できていない興味深い問題です。

インターネットを介してサーバーと通信するクライアントを作成しています。RAD Studio 2007 ネイティブ パーソナリティを使用して、Indy 10 で TIdTcpClient Internet Direct (Indy) コンポーネントを使用しています。

サーバーからデータを取得するために、SSL を使用してポート 443 経由で HTTP 要求を発行します。要求の詳細は HTTP メッセージ本文に含まれています。ここまでは順調ですね。このコードは、1 つの例外を除いて、まるで魔法のように機能します。

サーバーから約 336 KB の応答を生成する必要がある 1 つの要求を送信しています (HTTP 応答ヘッダーには Content-Length: 344795 が含まれています)。問題は、320KB しか返ってこないことです。XML 形式の応答は、XML 要素の途中で明らかに切り捨てられています。

価値のあることとして、XML は単純なテキストです。切り捨ての原因となる特殊文字はありません。私の TIdTcpClient コンポーネントは、部分的な応答を受信した後、サーバーが接続を正常に閉じたことを報告するだけです (これは、切り捨てられていないものであっても、すべての応答が完了すると予想されるため、問題ではありません)。

応答も数キロバイトを超える同じサーバーに対してほぼ同じ呼び出しを行うことができ、これらはすべて正常に機能します。私が行う 1 つの要求では約 850 KB が返され、別の要求では約 300 KB が返されます。

つまり、特定の 1 つの要求でのみこの問題が発生します。多数ある他のすべての要求は、完全な応答を受け取ります。

私はサービスの作成者と話し、私の要求の例を提供しました。彼はその要求が正しいと報告した. 彼はまた、彼のサーバーに同じリクエストを発行すると、完全な応答が得られるとも言いました。

私は途方に暮れています。サービスの作成者が間違っていて、実際にはその側の応答に問題があるか、または私の要求に何か特別なものがあります。

私が見逃している解決策はありますか?他の多くの読み取りメカニズム (ReadString、ReadStrings、ReadBytes など) も使用しましたが、すべて同じ結果 (320KB マークでのこの 1 つの特定の応答の切り捨て) を生成することに注意してください。

コードはおそらく関係ありませんが、とにかく含めます。申し訳ありませんが、専有情報が含まれているため、XML リクエストを含めることができません。(ReadTimeout は 20 秒に設定されていますが、リクエストは約 1 秒で返されるため、タイムアウトの問題ではありません。)

function TClient.GetResponse(PayloadCDS: TClientDataSet): 文字列;
変数
  s: 文字列;
始める
  試す
    試す
      s := GetBody(ペイロードCDS);
      IdTcpClient1.Host := ホスト;
      IdTcpClient1.Port := StrToInt(ポート);
      IdTcpClient1.ReadTimeout := ReadTimeout;
      IdTcpClient1.Connect;
      IdTcpClient1.IOHandler.LargeStream := True;
      //IdTcpClient1.IOHandler.RecvBufferSize := 2000000;
      IdTcpClient1.IOHandler.Write(s);
      結果:= IdTcpClient1.IOHandler.AllData;
    を除外する
      E: EIdConnClosedGracefully do
      始める
         //例外を食べる
      終わり;
      on e: 例外 do
      始める
        高める;
      終わり;
    終わり;
  最後に
    IdTcpClient1.Connected の場合
      IdTcpClient1.Disconnect;
  終わり;
終わり;
4

2 に答える 2

1

HTTP リクエストを送信しているため、TIdTCPClient コンポーネントを直接使用するのではなく、TIdHTTP コンポーネントを使用する必要があります。TIdHTTP が管理する HTTP プロトコルには多くの詳細があり、引き続き TIdTCPClient を直接使用する場合は手動で処理する必要があります。

TIdTCPClient を直接使用し続ける場合は、少なくとも TIdIOHandler.AllData() メソッドの使用を停止する必要があります。「Content-Length」応答ヘッダーを抽出し (ヘッダーを TStringList または TIdHeaderList に Capture() してから、その Values[] プロパティを使用できます)、実際に報告されたバイト数を TIdIOHandler.ReadString() または TIdIOHandler.ReadStream に渡します。 (). これにより、応答の最後にサーバーが切断されたために、読み取り I/O が途中で読み取りを停止しないようにすることができます。

于 2009-08-14T20:48:28.383 に答える
0

最初の質問で述べたように、この接続には SSL が使用されます。これには、IdTcpClient コンポーネントの IOHandler プロパティに割り当てる IdSSLIOHandlerSocketOpenSSL コンポーネントを使用する必要があります。これらはすべて私のオリジナルの設計であり、前述したように、私の要求の多くは正しく対応されています。

週末に、不完全な応答を返す別の要求を発見しました。元の HTTP 要求をキャプチャし、SOAP UI というツールを使用して、その要求をサーバーに送信しました。SOAP UI を使用して、サーバーは完全な応答を返しました。明らかに、サーバーではなくクライアントに問題がありました。

さまざまなプロパティをいじるだけで、最終的に解決策を見つけました。問題を最終的に修正したプロパティは、IdSSLIOHandlerSocketOpenSSL クラスの SSLOptions プロパティにありました。SSLOptions.Method のデフォルト値は sslvSSLv2 です。Method を sslvSSLv23 に変更すると、すべての応答が完全に返されました。

この変更を行う前に、すべてではなく一部の回答を完全に取得できた理由は、まだ謎です。それにもかかわらず、 IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method を sslvSSLv23 に設定すると、私の問題は解決しました。

Remy Lebeau さん、ご提案ありがとうございます。

于 2009-08-17T21:58:45.183 に答える