3

我终于让我的 Delphi 应用程序通过 HTTPS 使用带有 Synapse 库的直接套接字发送数据。

但是,我无法确定返回给我的数据的大小。

目前,我有以下代码:

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 和我从 synapse 尝试过的其他功能不适用于我的目的。

如何检查 - 使用带有 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 回答