Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   TcpClient - Antwort XML unvollständig. (https://www.delphipraxis.net/157527-tcpclient-antwort-xml-unvollstaendig.html)

Garfield 14. Jan 2011 22:09

TcpClient - Antwort XML unvollständig.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Mit Tcpclient wird an die Fritzbox diese Nachricht geschickt:
Code:
POST /upnp/control/WANIPConn1 HTTP/1.1
Host: fritz.box:49000
Accept: */*
Content-Type: text/xml
SoapAction:urn:schemas-upnp-org:service:WANIPConnection:1#GetStatusInfo
Content-Length: 263

<?xml version="1.0"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:GetStatusInfo xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1" />
</s:Body>
</s:Envelope>
Mit diesem Code
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  TcpClient1 : TTcpClient;
begin
  TcpClient1            := TTcpClient.Create(nil);
  TcpClient1.RemoteHost := Edit1.Text;
  TcpClient1.RemotePort := Edit2.Text;
  TcpClient1.OnReceive := TcpReceive;
  TcpClient1.OnError   := TcpError;

  TcpClient1.Connect;

  try
    TcpClient1.Sendln(Memo2.Text);
  finally
    TcpClient1.Disconnect;
  end;

  TcpClient1.Destroy;
end;

procedure TForm1.TcpReceive(Sender: TObject; Buf: PAnsiChar; var DataLen: Integer);
begin
  Memo1.Lines.Add(Buf);
end;

procedure TForm1.TcpError(Sender: TObject; SocketError: Integer);
begin
  ShowMessage(Format('Fehler: %d', [SocketError]));
end;
erhalte ich als Ergebnis:
Code:
HTTP/1.1 200 OK
DATE: Fri, 14 Jan 2011 21:51:41 GMT
SERVER: FRITZ!Box Fon WLAN 7320 UPnP/1.0 AVM FRITZ!Box Fon WLAN 7320 (UI) 100.04.88
CONNECTION: keep-alive
CONTENT-LENGTH: 430
CONTENT-TYPE: text/xml; charset="utf-8"
EXT:

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
<u:GetStatusInfoResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewConnectionStatus>Connected</NewConne
Da fehlt leider etwas.

Man kann SendLn und ReceiveLn so oft aufrufen bis die Datei vollständig ist. Das sieht dann so aus:
Code:
HTTP/1.1 200 OK
DATE: Fri, 14 Jan 2011 22:02:45 GMT
SERVER: FRITZ!Box Fon WLAN 7320 UPnP/1.0 AVM FRITZ!Box Fon WLAN 7320 (UI) 100.04.88
CONNECTION: keep-alive
CONTENT-LENGTH: 430
CONTENT-TYPE: text/xml; charset="utf-8"
EXT:

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
<u:GetStatusInfoResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewConnectionStatus>Connected</NewConnectionStatus>
<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>
<NewUptime>35312</NewUptime>
</u:GetStatusInfoResponse>
</s:Body> </s:Envelope>HTTP/1.1 200 OK
Was mache ich falsch? Es muss doch möglich sein, die ganze Datei sofort mit einem Aufruf zu erhalten?

marabu 15. Jan 2011 13:49

AW: TcpClient - Antwort XML unvollständig.
 
Hallo Garfield,

eventuell ist das von Dir angehängte Projekt ja anders aufgebaut, aber der in deinem Beitrag gezeigte Delphi-Code sieht irgendwie falsch aus. Lies mal die Antwort von Remy Lebeau auf eine andere Frage zur Komponente TTCPClient.

Grüße vom marabu

rollstuhlfahrer 15. Jan 2011 13:55

AW: TcpClient - Antwort XML unvollständig.
 
Ich glaube mal ganz einfach, das würde ich auch ohne den Verweis in dem Post drüber denken, dass du Nach dem Send() viel zu schnell dein Disconnect() aufrufst. Der TCP-Client beendet dann schon die Verbindung, bevor alles da ist, was da sein sollte. Oder er zeigt dir nur das erste von 2 Paketen an. Das 2. Paket wird dann von dir ignoriert, weil du schon die Verbindung wieder geschlossen hast.

Bernhard

Garfield 15. Jan 2011 15:10

AW: TcpClient - Antwort XML unvollständig.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von marabu (Beitrag 1074867)
..., aber der in deinem Beitrag gezeigte Delphi-Code sieht irgendwie falsch aus. ...

Ich habe beim bereinigen versehentlich nach dem SendLn die Zeile mit dem ReceiveLn entfernt. Nach Remy Lebeau kann eigentlich keine vernünftige Antwort zurückkommen. :(

Zitat:

Zitat von rollstuhlfahrer (Beitrag 1074868)
.. Der TCP-Client beendet dann schon die Verbindung, bevor alles da ist, was da sein sollte. ...

Kann sehr gut sein.

Wenn ich die Antwort zeilenweise bis zur zweiten Leerzeile lese:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  TcpClient1 : TTcpClient;
  temp : String;
  count : Integer;
begin
  Button1.Enabled := False;
  Memo1.Clear;

  TcpClient1            := TTcpClient.Create(nil);
  TcpClient1.RemoteHost := Edit1.Text;
  TcpClient1.RemotePort := Edit2.Text;
  TcpClient1.OnError   := TcpError;
  try
    if TcpClient1.Connect
    then begin
      TcpClient1.SendLn(Memo2.Text);
      temp := TcpClient1.Receiveln();
      count := 0;
      while count <= 1
      do begin
        Memo1.Lines.Add(temp);
        temp := TcpClient1.Receiveln;
        if Length(temp) = 0
        then inc(count);
      end;
    end;
  finally
    TcpClient1.Close;
    TcpClient1.Destroy;
  end;

  Button1.Enabled := True;
end;
bekomme ich sofort:
Code:
HTTP/1.1 200 OK
DATE: Sat, 15 Jan 2011 15:05:35 GMT
SERVER: FRITZ!Box Fon WLAN 7320 UPnP/1.0 AVM FRITZ!Box Fon WLAN 7320 (UI) 100.04.88
CONNECTION: keep-alive
CONTENT-LENGTH: 430
CONTENT-TYPE: text/xml; charset="utf-8"
EXT:
und etwa 30 s später die letzte Zeile:
Code:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
<u:GetStatusInfoResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewConnectionStatus>Connected</NewConnectionStatus>
<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>
<NewUptime>47504</NewUptime>
</u:GetStatusInfoResponse>
</s:Body> </s:Envelope>

Garfield 15. Jan 2011 18:45

AW: TcpClient - Antwort XML unvollständig.
 
Mit TIdTcpClient kommt man auch nicht weiter.

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  TcpClient : TIdTcpClient;
  temp : String;
  count : Integer;
begin
  Memo1.Clear;

  TcpClient     := TIdTcpClient.Create(nil);
  TcpClient.Host := Edit1.Text;
  TcpClient.Port := StrToInt(Edit2.Text);
  try
    TcpClient.Connect;
    if TcpClient.Connected
    then begin
      if TcpClient.IOHandler.Connected
      then begin
        TcpClient.IOHandler.Write(Memo2.Text);
        temp := TcpClient.IOHandler.ReadLn;
        count := 0;
        while count <= 1
        do begin
          Memo1.Lines.Add(temp);
          temp := TcpClient.IOHandler.ReadLn;
          if Length(temp) = 0
          then inc(count);
        end;
      end;
    end;
  finally
    TcpClient.Disconnect;
    TcpClient.Destroy;
  end;
end;
Das XML wird zwar in mehreren Zeilen zurückgegeben, aber bei der letzten Zeile hängt es dann.

rollstuhlfahrer 15. Jan 2011 18:59

AW: TcpClient - Antwort XML unvollständig.
 
Ich bin mir jetzt nicht ganz sicher, aber sollte nicht IdHTTPClient ausreichen?

Bernhard

Klaus01 15. Jan 2011 19:29

AW: TcpClient - Antwort XML unvollständig.
 
Guten Abend,

wenn aus dem Buffer mit readeLn gelesen wird,
dann wird nur eine Zeile (bis zum ersten CR LF) gelesen.

Ich meine es wäre besser, solange aus dem Buffer zu lesen
bis InputBufferIsEmpty = true ist.

Grüße
Klaus

Garfield 15. Jan 2011 20:35

AW: TcpClient - Antwort XML unvollständig.
 
Einen IdHTTPClient finde ich nicht, nur den IdHTTPseerver. :?:

Zitat:

Zitat von Klaus01 (Beitrag 1074940)
... bis InputBufferIsEmpty = true ist.

Damit kann ich leider nichts anfangen. :oops:

Klaus01 15. Jan 2011 20:46

AW: TcpClient - Antwort XML unvollständig.
 
.. hatte mir das in etwa so vorgestellt:

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  TcpClient : TIdTcpClient;
  temp : String;
  count : Integer;
begin
  Memo1.Clear;

  TcpClient := TIdTcpClient.Create(nil);
  TcpClient.Host := Edit1.Text;
  TcpClient.Port := StrToInt(Edit2.Text);
  try
    TcpClient.Connect;
    if TcpClient.Connected then
      begin
        if TcpClient.IOHandler.Connected then
          begin
            TcpClient.IOHandler.Write(Memo2.Text);
             
            while not tcpClient.IOHandler.InputBufferIsEmpty do
              begin
               temp := TcpClient.IOHandler.ReadLn;
               Memo1.Lines.Add(temp);
              end;
          end;
      end;
  finally
    TcpClient.Disconnect;
    TcpClient.Destroy;
  end;
end;
[ungetestet]

Grüße
Klaus

rollstuhlfahrer 15. Jan 2011 21:24

AW: TcpClient - Antwort XML unvollständig.
 
Zitat:

Zitat von Garfield (Beitrag 1074953)
Einen IdHTTPClient finde ich nicht, nur den IdHTTPseerver. :?:

Das Ding heißt leider nur IdHTTP (nicht -Client).

Bernhard

Garfield 15. Jan 2011 23:40

AW: TcpClient - Antwort XML unvollständig.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von Klaus01 (Beitrag 1074954)
.. hatte mir das in etwa so vorgestellt:

Mit zwei kleinen Einfügungen geht es. Allerdings fehlt wieder die letzte Zeile mit den zwei schließenden Tags. Nach einer Weile gibt es eine Meldung "Connection Closed Gracefully.". Wenn man einen Fehler in das Script einbaut, bekommt man einen Fehler mit der gesamten Meldung:
Code:
HTTP/1.1 500 Internal Server Error
DATE: Sat, 15 Jan 2011 23:17:24 GMT
SERVER: FRITZ!Box Fon WLAN 7320 UPnP/1.0 AVM FRITZ!Box Fon WLAN 7320 (UI) 100.04.88
CONNECTION: keep-alive
CONTENT-LENGTH: 434
CONTENT-TYPE: text/xml; charset="utf-8"

<?xml version="1.0"?>
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<s:Fault>
<faultcode>s:Client</faultcode>
<faultstring>UPnPError</faultstring>
<detail>
<UPnPError xmlns="urn:schemas-upnp-org:control-1-0">
<errorCode>401</errorCode>
<errorDescription>invalid action</errorDescription>
</UPnPError>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
Zitat:

Zitat von rollstuhlfahrer (Beitrag 1074960)
Das Ding heißt leider nur IdHTTP (nicht -Client).

Bei der Suche musste ich feststellen, das IdHTTP fast immer als IdHTTPClient bezeichnet wird. Das Problem ist dabei, dass eine URL benötigt wird, welche ich nicht habe bzw nicht zusammenbekomme. Es gibt einen Host, einen Port und eine ControlURL.

Garfield 16. Jan 2011 00:24

AW: TcpClient - Antwort XML unvollständig.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Auf der Suche nach der Fehlermeldung fand ich diese Lösung: Die gesendete und empfangene Nachricht enden mit dem selben schließenden Tag. Deshalb funktioniert dies:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var
  TcpClient : TIdTcpClient;
begin
  Memo1.Clear;

  TcpClient     := TIdTcpClient.Create(nil);
  TcpClient.Host := 'fritz.box';
  TcpClient.Port := 49000;
  try
    TcpClient.Connect;
    if TcpClient.Connected
    then begin
      if TcpClient.IOHandler.Connected
      then begin
        TcpClient.IOHandler.Write(Memo3.Text);

        Memo1.Text := TcpClient.IOHandler.WaitFor(Memo3.Lines.Strings[Memo3.Lines.Count - 1]);
        Memo1.Lines.Add(Memo3.Lines.Strings[Memo3.Lines.Count - 1]);
      end;
    end;
  finally
    TcpClient.Disconnect;
    TcpClient.Destroy;
  end;
end;
Code:
HTTP/1.1 200 OK
DATE: Sun, 16 Jan 2011 00:20:28 GMT
SERVER: FRITZ!Box Fon WLAN 7320 UPnP/1.0 AVM FRITZ!Box Fon WLAN 7320 (UI) 100.04.88
CONNECTION: keep-alive
CONTENT-LENGTH: 430
CONTENT-TYPE: text/xml; charset="utf-8"
EXT:

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
<u:GetStatusInfoResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewConnectionStatus>Connected</NewConnectionStatus>
<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>
<NewUptime>80745</NewUptime>
</u:GetStatusInfoResponse>
</s:Body>
</s:Envelope>
Das einzige, was etwas stört, sind die "falschen" Zeilenschaltungen im XML.

Anscheinend ist es ein Fehler in der verwendeten Indy-Version.

Garfield 16. Jan 2011 11:02

AW: TcpClient - Antwort XML unvollständig.
 
Der Fehler scheint in meiner Fritzbox zu stecken. Bei der Antwort fehlt in der letzten Zeile das LF (#10). Deshalb wartet Indy auf weitere Daten bis die Verbindung geschlossen wird.
___

Nachtrag:

Oben hatte ich die Indy 10-Version welche bei TurboDelphi dabei war verwendet. Bei der aktuellen Indy 10 wird beim WaitFor der angegebene Text mit zurück gegeben.

Sir Rufo 16. Jan 2011 11:12

AW: TcpClient - Antwort XML unvollständig.
 
Bei einer vernünftigen Kommunikation sollte am Anfang die Länge der übertragenen Nachricht übermittelt werden, dann ist das Ende Zeichen sowas von überflüssig.

Schau dir die Nachricht mal als Hex an, ob da am Anfang nicht diese Information mitkommt.
Im Header steht aber auch eine Information zum Anzahl der Zeichen.

Versuche ansonsten diese auszuwerten

Garfield 16. Jan 2011 12:53

AW: TcpClient - Antwort XML unvollständig.
 
Indy reserviert für die Antwort 32 kB. Nach der Ermittlung der Antwortlänge wird die Antwort in einem entsprechend großen Buffer geschrieben. Im Beispiel sind es 663 Byte.

ReadLn ruft ReadLn(LF) auf und die function TIdBuffer.IndexOf(const AString: string; AStartPos: Integer): Integer; ermittelt die Position des nächsten LF. Beim letzten String wird die Funktion einfach beendet und anstelle der Länge des letzten Strings -1 zurückgegeben.

Garfield 16. Jan 2011 13:20

AW: TcpClient - Antwort XML unvollständig.
 
Delphi-Quellcode:
function TIdBuffer.IndexOf(const ABytes: TIdBytes; AStartPos: Integer): Integer;
var
  i, j, LEnd, BytesLen: Integer;
  LFound: Boolean;
begin
  Result := -1;
  // Dont search if it empty
  if Size > 0 then begin
    EIdException.IfTrue(Length(ABytes) = 0, RSBufferMissingTerminator);
    EIdException.IfNotInRange(AStartPos, 0, Size - 1, RSBufferInvalidStartPos);
    BytesLen := Length(ABytes);
    LEnd := FHeadIndex + Size;
    for i := FHeadIndex + AStartPos to LEnd - BytesLen do begin
      LFound := True;
      for j := 0 to BytesLen - 1 do begin
        if i + j < LEnd then begin
          if FBytes[i + j] <> ABytes[j] then begin
            LFound := False;
            Break;
          end;
        end
        else
          Break;
      end;
      if LFound then begin
        Result := i - FHeadIndex;
        if Result <> -1 then
          Break;
      end;
    end;
  end;
end;
Geändert in
Delphi-Quellcode:
function TIdBuffer.IndexOf(const ABytes: TIdBytes; AStartPos: Integer): Integer;
var
  i, j, LEnd, BytesLen: Integer;
  LFound: Boolean;
begin
  Result := -1;
  // Dont search if it empty
  if Size > 0 then begin
    EIdException.IfTrue(Length(ABytes) = 0, RSBufferMissingTerminator);
    EIdException.IfNotInRange(AStartPos, 0, Size - 1, RSBufferInvalidStartPos);
    BytesLen := Length(ABytes);
    LEnd := FHeadIndex + Size;
    for i := FHeadIndex + AStartPos to LEnd - BytesLen do begin
      LFound := True;
      for j := 0 to BytesLen - 1 do begin
        {
        *  Beim letzten Zeichen des Buffer ist auf jeden Schluss.
        }
        if i + j < LEnd - 1 then begin
          if FBytes[i + j] <> ABytes[j] then begin
            LFound := False;
            Break;
          end;
        end
        else
          Break;
      end;
      if LFound then begin
        Result := i - FHeadIndex;
        if Result <> -1 then
          Break;
      end;
    end;
  end;
end;
und es funktioniert.

Garfield 16. Jan 2011 20:34

AW: TcpClient - Antwort XML unvollständig.
 
Zitat:

Zitat von Garfield (Beitrag 1075040)
und es funktioniert.

Doch nicht ganz. Das letzte Zeichen fehlt.

Da muss dann noch die function TIdIOHandler.ReadLn(ATerminator: string; ATimeout: Integer = IdTimeoutDefault; AMaxLineLength: Integer = -1): string; ein wenig angepasst werden.
Delphi-Quellcode:
  // Extract actual data
  Result := FInputBuffer.Extract(LTermPos + Length(ATerminator));
  if (ATerminator = LF) and (LTermPos > 0) then begin
    {
    *  Wenn beim letzten String das LF fehlt, würde das letzte Zeichen abgeschnitten.
    }
    if (Result[LTermPos + 1] <> CR) and (Result[LTermPos + 1] <> LF)
    then inc(LTermPos);

    if Result[LTermPos] = CR then begin
      Dec(LTermPos);
    end;
  end;
  SetLength(Result, LTermPos);
end;
Wobei ich mich frage, ob man da ein
Delphi-Quellcode:
  // Extract actual data
  Result := FInputBuffer.Extract(LTermPos + Length(ATerminator));
  Result := TrimRight(Result);
end;
nicht ausreichend wäre.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:59 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz