3

私は最終的に、Synapse ライブラリで直接ソケットを使用して HTTPS 経由でデータを送信する Delphi アプリケーションを手に入れました。

しかし、返されたデータのサイズを判断するのに苦労しています。

現在、次のコードがあります。

Socket     := TTCPBlockSocket.Create;
status     := TStringList.Create;  

status.Append('LineBuffer length: ' + IntToStr(Length(Socket.LineBuffer)));
status.Append('Waiting bytes: ' + IntToStr(Socket.WaitingDataEx));
status.Append('recvBufferStr: ' + CRLF + Socket.RecvBufferStr(240, 25000) );
status.Append('LastError = ' + Socket.LastErrorDesc);
status.Append('Error code: ' + IntToStr(Socket.LastError));
Memo1.Lines.AddStrings(status); 

そして、Memo1 で次のように取得します。

socket lastError = 0
LineBuffer length: 0
Waiting bytes: 0
recvBufferStr: 
HTTP/1.1 200 OK
Date: Thu, 22 Dec 2011 01:06:07 GMT
Server: Apache
Content-Length: 237
Connection: close
Content-Type: text/plain; charset=utf-8

[***The returned Data***]

Socket.RecvBufferStrの最初のパラメーター (受信する長さ) が大きすぎると、サーバーが送信を完了してもデータを待機しているため、winsock エラー 10054 が発生します。

短すぎる場合 (ほとんどの場合そうです)、指定された量のデータしか取得できません。

待機中のバイト数とラインバッファの長さが 0 を示しているので (それが longInt であり、IntToStr を実行しているためかどうかはわかりません)、データの量を確認する方法ではないと思います。また、 CanReadと CanReadEx を使用してみましたが、役に立ちませんでした。

次のようなことをしたいと思います: Socket.RecvBufferStr ([受信する長さ]、[すべてのデータが受信されるまで] または 25000)。

別の機能がある場合はそれで問題ありませんが、シナプスから試したHTTPSendやその他の機能は私の目的では機能しないため、TTCPBlockSocketに固執したいと思います。

Delphi/Pascal でSynapseライブラリのTTCPBlockSocketソケットを使用して、サーバーから返されるデータの量を確認するにはどうすればよいですか?

4

2 に答える 2

2

次のコードを試してください。Content-Length応答ヘッダーからを取得し、コンテンツ自体を読み取る方法を示します。

HTTP プロトコルはそれほど簡単ではないことに注意してください。ただし、独自のサーバーまたはその仕組みを知っているサーバーと通信する場合は、これを参考にしてください。このコードを使用するときは、バイナリを含めることを忘れないでくださいOpenSSL(バージョン 0.9.8 は確実に Synapse と互換性があります)。

uses
  blcksock, ssl_openssl;

function HTTPGetText(const Host: string): AnsiString;
var
  ContentLength: Integer;
  HTTPHeader: AnsiString;      
  TCPSocket: TTCPBlockSocket;
begin
  Result := '';

  TCPSocket := TTCPBlockSocket.Create;
  try
    // initialize TTCPBlockSocket
    TCPSocket.ConvertLineEnd := True;
    TCPSocket.SizeRecvBuffer := c64k;
    TCPSocket.SizeSendBuffer := c64k;

    // connect to the host, port 443 is default for HTTP over SSL
    TCPSocket.Connect(Host, '443');
    // check for socket errors
    if TCPSocket.LastError <> 0 then
      Exit;

    // server name identification
    TCPSocket.SSL.SNIHost := Host;
    // initialize and connect over OpenSSL
    TCPSocket.SSLDoConnect;
    // server name identification
    TCPSocket.SSL.SNIHost := '';
    // check for socket errors
    if TCPSocket.LastError <> 0 then
      Exit;

    // build the HTTP request header
    HTTPHeader :=
      'GET / HTTP/1.0' + CRLF +
      'Host: ' + Host + ':443' + CRLF +
      'Keep-Alive: 300' + CRLF +
      'Connection: keep-alive' + CRLF +
      'User-Agent: Mozilla/4.0' + CRLF + CRLF;

    // send the HTTP request
    TCPSocket.SendString(HTTPHeader);
    // check for socket errors
    if TCPSocket.LastError <> 0 then
      Exit;

    // read the response status, here some servers might give you the content
    // note, that we are waiting in a loop until the timeout or another error
    // occurs; if we get some terminated line, we break the loop and continue
    // to check if that line is the HTTP status code
    repeat
      HTTPHeader := TCPSocket.RecvString(5000);
      if HTTPHeader <> '' then
        Break;
    until
      TCPSocket.LastError <> 0;

    // if the line we've received is the status code, like 'HTTP/1.1 200 OK'
    // we will set the default value for content length to be read
    if Pos('HTTP/', HTTPHeader) = 1 then
    begin
      ContentLength := -1;

      // read the response header lines and if we find the 'Content-Length' we
      // will store it for further content reading; note, that again, we are
      // waiting in a loop until the timeout or another error occurs; if the
      // empty string is received, it means we've read the whole response header
      repeat
        HTTPHeader := TCPSocket.RecvString(5000);
        if Pos('Content-Length:', HTTPHeader) = 1 then
          ContentLength := StrToIntDef(Trim(Copy(HTTPHeader, 16, MaxInt)), -1);
        if HTTPHeader = '' then
          Break;
      until
        TCPSocket.LastError <> 0;

      // and now let's get the content, we know it's size, so we just call the
      // mentioned RecvBufferStr function
      if ContentLength <> -1 then
        Result := TCPSocket.RecvBufferStr(ContentLength, 5000);
    end;

    TCPSocket.CloseSocket;

  finally
    TCPSocket.Free;
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  Memo1.Text := HTTPGetText('www.meebo.com');
end;
于 2011-12-22T04:10:38.200 に答える
1

WaitingDataEx を再度テストしているときに、TLama が言及したように、RecvBufferStr の後に WaitingDataEx を出力する行を入れることにしました。

IntToStr(WaitingDataEx) が機能し、残りのデータを表示しました。

WaitingDataEx は、その特定の瞬間に待機中のデータをチェックします。データの送信からデータの検索までの実際の一時停止がなかったため、受信するデータがまだないことがわかりました。

次のコードで問題を解決しました。

httpRsp := httpRsp + Socket.RecvBufferStr(1, 25000);
httpRsp := httpRsp + Socket.RecvBufferStr(Socket.WaitingDataEx, 100); 
  1. これは、1 バイトのデータが受信されるのを待ってから、そのデータを文字列変数 httpRsp に追加します。
  2. 次に、Socket.WaitingDataEx を使用して残りのバイトを読み取ります。これは、最初の RecvBufferStr コマンドが、受信するバイトがあることを確認するための待機を既に行っているためです。

上に編集

上記のコードは、私が通信している特定のサーバーから壊れましたが、理由はわかりません. 通信しているサーバーからのコンテンツではなく、ヘッダーのみを受信します。以下のコードで問題を解決しました。

httpRsp := httpRsp + Socket.RecvPacket(50000);
while Socket.WaitingDataEx > 0 do
  httpRsp := httpRsp + Socket.RecvPacket(50000); 
于 2011-12-22T17:38:15.350 に答える