Delphi + Synapse: Как проверить размер ответа с помощью TTCPBlockSocket - PullRequest
3 голосов
/ 22 декабря 2011

Наконец-то я получил приложение Delphi для отправки данных через прямые сокеты с библиотекой Synapse через HTTPS.

Однако у меня возникли проблемы с определением размера возвращаемых мне данных.

В настоящее время у меня есть следующий код:

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).

Если есть другая функция, это нормально, но я бы хотел использовать TTCPBlockSocket , так как HTTPSend и другие, которые я пробовал из синапса, не работают для моих целей.

Как я могу проверить - используя TTCPBlockSocket из библиотеки Synapse с Delphi / Pascal - сколько данных сервер возвращает мне?

Ответы [ 2 ]

2 голосов
/ 22 декабря 2011

Попробуйте следующий код.Он показывает, как получить 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;
1 голос
/ 22 декабря 2011

При повторном тестировании WaitingDataEx, как упоминал TLama, я решил поместить строку, которая выводит WaitingDataEx после RecvBufferStr.

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); 
...