Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi indy10 / TCPServer /TCPClient ->Datei versenden (https://www.delphipraxis.net/111416-indy10-tcpserver-tcpclient-datei-versenden.html)

cherry 3. Apr 2008 10:29

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Ok, Ist zwar jetz nicht DIE Lösung, sondern eine Umgehung des Problems. Aber trotzdem, wie geht das jetzt mit der Progressbar? ;-)

STS301 3. Apr 2008 11:56

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Delphi-Quellcode:
progressbar.min := 0;
 try
  //Anweisungen
  progressbar.Position:= 100;
finally

Der.Kaktus 3. Apr 2008 12:07

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
ich verweise auf meinen Tip im Beitrag #2 dieses Threads. Da steht alles beschrieben zwecks Progressbar beim Filetransfer. :coder2:

cherry 4. Apr 2008 06:08

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
hey... ich habs jetzt mal so gemacht. Es funktioniert allerdings nicht immer und nicht zuverlässig.
Was ich jetzt will ist: Der Client soll eine Datei an den Server schicken können aber vorab soll der name der zu schickenden Datei gesendet werden, damit dann der Server die Datei unter diesem Namen abspeichern kann. Dies hab ich mal probiert zu machen aber ich halte den Code für naja, unschön ist noch milde ausgedrückt.

Kurz: Wie kann ich verschiedene Sachen (Strings für Befehle, Anfragen und Meldungen und die Streams) kommunizieren ohne das es mir ein Durcheinander gibt? Ich kann ja nie wissen was ich gerade erhalten habe?

Mein momentaner CodeSalat:

Delphi-Quellcode:
procedure TForm1.EButton2Click(Sender: TObject);
var
  FStream: TFileStream;
begin
  try
    if OpenDialog1.Execute then
      begin
      if FileExists(OpenDialog1.FileName) then
      begin
        FStream := TFileStream.Create(OpenDialog1.FileName, fmOpenRead);
        TCPClient.Connect;
        TCPClient.IOHandler.WriteLn(ExtractName(Opendialog1.FileName));
        TCPClient.Disconnect;
        TCPClient.Connect;
        try
          TCPClient.IOHandler.Write(Fstream,0,true);
        finally
          FreeAndNil(FStream);
        end;
        if TCPClient.Connected then
          TCPClient.Disconnect;
      end
      else
        MessageDlg('file does not exist!', mtError, [mbOK], 0);
    end;
  except
    on e:Exception do
      MessageDlg(e.Message, mtError, [mbOK], 0);
  end;
end;

// TCP Server
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
  FSTream: TFileStream;
  df: String;
begin
  try
    if savefilepath = '' then
    begin
      savefilepath := DestinationEdit.Text+AContext.connection.IOHandler.ReadLn ;
      Memo1.Lines.Add('download file: "'+savefilepath+'" from '+AContext.Binding.PeerIP);
    end
    else
    begin
      try
        if FileExists(savefilepath) then
        begin
          if MessageDlg('overwrite existing file?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
          begin
            df := savefilepath;
            DeleteFile(df);
            FStream := TFileStream.Create(savefilepath, fmCreate);
            AContext.connection.IOHandler.ReadStream(fstream);
          end;
        end
        else
        begin
          if DirectoryExists(ExtractFilePath(savefilepath)) = false then
            CreateDir(ExtractFilePath(savefilepath));
          FStream := TFileStream.Create(savefilepath, fmCreate);
          AContext.connection.IOHandler.ReadStream(fstream);
        end;
      finally
        FreeAndNil(FStream);
      end;
      AContext.connection.Disconnect;
    end;
    savefilepath := '';
  except
    on e:Exception do
      MessageDlg(e.Message, mtError, [mbOK], 0);
  end;
end;

cherry 4. Apr 2008 11:56

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Liste der Anhänge anzeigen (Anzahl: 3)
hey leuts

ich habe nun mein testprogramm fertiggestellt, es hat aber noch einen Fehler! und ich finde ihn nicht heraus! bin schon seit stunden dran und mir gehen langsam die ideeen aus. Manche Dateien kann ich übertragen andere nicht, manchmal geht diese und manchmal aber nur die andere.

Vielleicht schauts mal jmd von euch an?! wär echt toll!

TEButton / THideBox)

Danke schon mal zum Voraus

Liebe Grüsse

Cherry

DataCool 6. Apr 2008 02:56

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Hi,

ich hab mir bis jetzt zwar nur Deinen "Codesalat" hier angeschaut,
aber was Du brauchst ist definitiv eine saubere Serverstruktur und
ein kleines eigenes Protokoll.

- Du solltest vor dem Stream ein Text Commando zum Server schicken mit allen für den Transfer
nötigen Information z.B. FILETRANSFER||AUTOEXEC.BAT||348||OVERRIDEONSERVER

- Dann solltest Du auf jeden Fall Informationen zu dem jeweiligen Client in AContext.Data abspeichern;
ich benutze dazu oft folgendes :

Delphi-Quellcode:
unit JFIndy10BaseClientData;

// copyright by DataCool (at) gmx dot net
// in case of modifications please let me know

interface

uses IdContext;

Const

// predefined Status numbers

  cCSUnknown         : Byte = 0;
  cCSJustConnected   : Byte = 1;
  cCSGoing2Disconnect : Byte = 2;
  cCSDisconnected    : Byte = 3;
  cCSWaiting4NextCmd : Byte = 4; // status after a noop cmd

type

  TIndyBaseClientData = class
    private
      // is a login necessary for this connection
      //fLoginRequired : Boolean;

      // login was successfull
      //fLoginOK       : Boolean;

      // is a NOOP Command for this connection required ?
      fNoopRequired : Boolean;

      // Last-Command
      fLastCmd       : String;

      // Last-Command as Integer, in case the server is working with integer commands
      fLastIntCmd    : Word;

      // Last command time, necessary together with noops to detect dead client connections
      fLastCmdTime   : TDateTime;

      // time of the connect of this connection
      fTimeOfConnect : TDateTime;
     
      // Status of this connection, maybe later on switch to type word, if a program requires more than 255 commands
      fStatus        : Byte;

      // Indy-Context of this connection
      fContext        : TIdContext;

      // procedures to set the last command,lastCmdTimd would be updated also
      procedure setStrCommand(Const Cmd : String);
      procedure setIntCommand(Const Cmd : Word);
    protected

    public
      Constructor Create(AContext : TIdContext);

      property Context : TIdContext read fContext;
      //property LoginRequired : boolean read fLoginRequired write fLoginRequired;
      //property LoginOk : boolean read fLoginOk write fLoginOk;
      property NoopRequired : Boolean read fNoopRequired write fNoopRequired;
      property LastCmd : String read fLastCmd write setStrCommand;
      property LastIntCmd : Word read fLastIntCmd write setIntCommand;
      property LastCmdTime : TDateTime read fLastCmdTime write fLastCmdTime;
      property TimeOfConnect : TDateTime read fTimeOfConnect write fTimeOfConnect;
      property Status : Byte read fStatus write fStatus;

      // isConnectionDead ?
  end;

implementation

uses SysUtils;

{ TIndyBaseClientData }

constructor TIndyBaseClientData.Create(AContext: TIdContext);
begin
  inherited Create;
  fContext := AContext;
  //fLoginRequired := true;
  //fLoginOK := false;
  fNoopRequired := false;
  fLastCmd := '';
  fLastIntCmd := 0;
  fLastCmdTime := 0;
  fTimeOfConnect := now;
  fStatus := cCSUnknown;
end;

procedure TIndyBaseClientData.setIntCommand(const Cmd: Word);
begin
  // dont compare cmd and fLastIntcommand, because perhaps the same cmd is coming 2 times
  fLastIntCmd := Cmd;
  fLastCmd := InttoStr(fLastIntCmd);
  // update also the last command time
  fLastCmdTime := now;
end;

procedure TIndyBaseClientData.setStrCommand(const Cmd: String);
begin
  // dont compare cmd and fLastcommand, because perhaps the same cmd is coming 2 times
  fLastCmd := Cmd;
  fLastIntCmd := StrToIntDef(fLastCmd,0);
  // update also the last command time
  fLastCmdTime := now;
end;

end.
Für die unterschiedlichen Projekte erstelle ich mir dann immer eine Klasse TClientData,
die von der oben genannten Klasse erbt/abgeleitet wird.

Im OnConnect des Servers erzeuge ich dann meine Klasse TClientData,
fülle diese mit Daten/Infos und lege Sie in AContext.Data ab.

Im OnExecute des Servers:
Delphi-Quellcode:
Var sUppCmd : String;
    sRawCmd : String;
    iPos   : Longint;
    sParam : String;
    tmpClient : TClientData;
begin
  // Prüfen, ob Client Daten vorhanden sind
  // bei Indy10 wird das OnExecute auch einmalig ausgeführt, wenn die Connection des Clients im OnConnect getrennt wurde
  if not assigned(AThread.Data) then begin
    AContext.Connection.Disconnect;
    exit;
  end;
  // Client-Daten zu auslesen
  try
    tmpClient := TClientData(AThread.Data);
  except
    AContext.Connection.Disconnect;
    exit;
  end;

  if (tmpClient.Status = cCSGoing2Disconnect) or (tmpClient.Status = cCSDisconnected) then begin
    AContext.Connection.Disconnect;
    exit;
  end;
  try
    // Daten im Buffer ?
    tmpClient.LastCmd := AContext.Connection.Socket.ReadLn(#$A,Settings.CmdReadTimeOut,Settings.CmdMaxLength);
  except
    tmpClient.LastCmd := '';
  end;
  // Da das OnExecute des Server immer wieder eintritt bis der Client nicht mehr verbunden ist kann der obere Teil als
  // allgemeingültig bezeichnet werden. Je nach empfangenen Daten/Kommando jetzt weiter vorgehen

  // z.B.
  Case tmpClient.LastIntCommand of
    //
  end;
  // oder
  if tmpClient.LastCmd = 'YXZ' then begin

  end;
  // ...
- Wichtig ! Im OnDisconnect müssen die Daten des Clients auch wieder freigegeben werden
Delphi-Quellcode:
Var tmpClient : TClientData;
begin
  // valid data in . data
  if Assigned(AContext.Data) then begin
    // try to cast the data to TClientData
    try
      tmpClient := TClientData(AContext.Data);
    except
      tmpClient := Nil;
    end;
    // Cast successfull ?
    if Assigned(tmpClient) then
      FreeAndNil(tmpClient)
    // !!!! very imported to set AContext.Data to nil !!!!!
    AContext.Data := Nil;
  end;
end;
Ich hoffe ^das^ hat erstal ein groben Überblick/Denkanstoss geschaffen,
wie man sowas realisieren könnte.
Außerdem sollte im Servercode kein MessageDlg verwendet werden,
ein Server sollte selbstständig und OHNE Interaktion mit dem User laufen.
Am besten sogar ganz ohne GUI als Dienst.

Greetz DataCool

STS301 6. Apr 2008 10:28

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Liste der Anhänge anzeigen (Anzahl: 1)
mit der Grafik musst du dir noch was einfallen lassen, beim 20 Zoll sieht das so aus:

busybyte 6. Apr 2008 13:40

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
http://www.indyproject.org/docsite/h...StreamVCL.html

Mann ist die Indyhilfe bescheiden...irgendwann erscheint der Button Home auf dem Link und dann eine Suchleiste,darin findet man z.B.
idStream.write und dort sieht man dann die benötigte Unit.

Topic Path: Symbol Reference > Types > TIdStream Type
ContentsIndexHome

PreviousUpNext
TIdStream Type
Collapse All

Implements a stream wrapper for the platforms supported by the Indy library.
File

IdObjs
Pascal

TIdStream = TStream;

Description

TIdStream is a TObject descendant that specifies a wrapper for stream classes. TIdStream isolates the differences between stream implementations for the platforms supported by the Indy library.

To minimize the number of overloaded methods required for TStream and .Net Stream arguments, TIdStream provides an implict converter as a class operator for VCL to .Net stream types.

TIdStream overrides provides methods used for reading and writing to the stream that isolate the differences between pointers and memory allocation for the .Net platform.
See Also

TIdBytes
Copyright © 1993-2006, Chad Z. Hower (aka Kudzu) and the Indy Pit Crew. All rights reserved.
Post feedback to the Indy Docs Newsgroup.



Uses idObjs; //ist ja auch völlig logisch, lol,ob die wohl Drogen konsumieren?

cherry 7. Apr 2008 10:11

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Zitat:

Zitat von DataCool
Ich hoffe ^das^ hat erstal ein groben Überblick/Denkanstoss geschaffen,
wie man sowas realisieren könnte.
Außerdem sollte im Servercode kein MessageDlg verwendet werden,
ein Server sollte selbstständig und OHNE Interaktion mit dem User laufen.
Am besten sogar ganz ohne GUI als Dienst.

Greetz DataCool

naja, also das mit den Dialogen im Servercode habe ich bereits selber rausbekommen und entsprechend geändert.

Ich hab mir also mal deine Unit "JFIndy10BaseClientData" nachgebaut und versucht zu implementieren. Dabei bin ich gleich auf ein Problem(chen) gestossen. Und zwar im OnExecute Event vom Servercode:


Delphi-Quellcode:
// Client-Daten zu auslesen
  try
    tmpClient := TClientData(AThread.Data);
  except
    AContext.Connection.Disconnect;
    exit;
  end;
Ich denke da hast du noch was von einer älteren Indy-version vermischt. Habe einfach "AThread.Data" in "AContext.Data" geändert. Aber jetzt das eigentliche Problem(chen):

Delphi-Quellcode:
try
    // Daten im Buffer ? 
    tmpClient.LastCmd := AContext.Connection.Socket.ReadLn(#$A,Settings.CmdReadTimeOut,Settings.CmdMaxLength);
  except
    tmpClient.LastCmd := '';
  end;
Ich kann nicht auf "Settings" zugreiffen (Nicht def. Bezeichner)... woher genau soll das kommen? hab ich was verpasst?

Ansonsten finde ich den Ansatzt sehr gut und hoffe den so implementieren zu können das alles nach meinen Vorstellungen klappt.
Noch was, wieso soll das Ganze am besten ohne GUI und noch besser als Dienst laufen?! Wenn ich ein anständiges exception handling
mache sollte das doch kein Problem sein?!


Nun noch eine Frage zum Senden der Datei

Kann ich das jetzt so machen:

Delphi-Quellcode:
  FStream := TFileStream.Create(uploadfilepath, fmOpenRead or fmShareDenyWrite);
  TCPClient.Connect;
  try
    TCPClient.IOHandler.Write(Fstream,0,true);
  finally
    FreeAndNil(FStream);
  end;
oder muss ich den Stream umwandeln? Wenn ja wie und in was? vielleicht so?:

Delphi-Quellcode:
// Send TCP Stream
procedure TForm1.TCPSendStream(Stream: TFileStream);
var
  IdStream: TIdStream;
begin
  IdStream:= TIdStream.Create;
  IdStream.CopyFrom(Stream,SizeOf(Stream));
  TCPClient.Connect;
  TCPClient.IOHandler.Write(IdStream,0,true);
  TCPClient.Disconnect;
end;
Naja ich dank euch schon mal allen für die tolle Hilfe...
wär genial wenn ihr mich noch ein bisschen begleiten würdet, bis ich dann auf eigenen Füssen stehen kann... hehe :cheers:

DataCool 7. Apr 2008 13:49

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Hi Cherry,

Du hast recht ich habe ^den^ Source mal eben von Indy9 auf Indy10 portiert,
damit Du was damit anfangen kannst.

Hier eben die kleinen Portierbugs behoben:

Delphi-Quellcode:
  // Client-Daten zu auslesen
  try
    tmpClient := TClientData(AContext.Data);
  except
    AContext.Connection.Disconnect;
    exit;
  end;
Delphi-Quellcode:
  try

    // Settings.CmdReadTimeOut <-- meine Einstellungsklasse der WErt des ReadlnTimeOutsfür Kommandos
    // Settings.CmdMaxLength <-- meine Einstellungsklasse der Wert der maximalen Zeichenlänge mein ReadLn

    // Daten im Buffer ? 5000 ms = 5 Sekunden ReadLn-Timeout, maximal 1024 lesen
    // die Parameter beim ReadLn sind optional und müssen nicht zwingend verwendet werden
    tmpClient.LastCmd := AContext.Connection.Socket.ReadLn(#$A,5000,1024);
  except
    tmpClient.LastCmd := '';
  end;
Zum Senden einer Datei via Stream; Ich kann Dir jetzt nicht mit 100% Sicherheit sagen wie's gemacht wird,
weil ich meine akztuellen Projekte alle noch in Indy9 habe und bald erst mit dem portieren anfange.
Was ich aber festgestellt habe ist das es in Indy10 kein "WriteStream" mehr gibt, obwohl ein "ReadStream"
gibt. Ich habe aber auch gesehen das es bei Indy10 jetzt ein WriteFile gibt und wenn man sich den Source dazu anschaut :

Delphi-Quellcode:
function TIdIOHandler.WriteFile(const AFile: String; AEnableTransferFile: Boolean): Int64;
var
//TODO: There is a way in linux to dump a file to a socket as well. use it.
  LStream: TIdStream;
begin
  EIdFileNotFound.IfFalse(Sys.FileExists(AFile), Sys.Format(RSFileNotFound, [AFile]));
  LStream := TReadFileExclusiveStream.Create(AFile); try
      Write(LStream);
      Result := LStream.Size;
  finally Sys.FreeAndNil(LStream); end;
end;
^^ Da könntest Du Dir die Vorgehensweise abschauen.

Jetzt nochmal zu der Frage warum ich keine GUI verwenden würde :

Wenn es nachher ein produktiver Server ist, soll er vor allem eins : Laufen ohne abzustürzen ;-)
Eine hohe Ausfallsicherheit erreichst man unter anderem auch, indem man die Fehlerquellen minimiert,
wenn Du mit einer GUI in einem Multi-Threaded Server arbeitest, solltest alle Änderungen an der GUI
nur syncronisiert vorgenommen werden, ansonsten könne recht "unschöne" Sachen passieren und Du denkst
Dein Server läuft noch obwohl er nicht mehr richtig arbeitet.
Klar kannst Du, wenn Du sauber programmierst das ganze auch mit GUI machen, ich wollte Dich nur auf die Gefahren hinweisen ;-)

Versteh mich nicht falsch ich habe zu meinen "Servern" auch eine GUI aber das ganze läuft bei mir so :
- Server wird als Dienst geschrieben, hat keine Gui und keine Interaktion mit dem Desktop
- Im Server wird ein zusätzlicher Port nur lokal geöffnet
- Dann schreibe ich meine "GUI zum Server" als eigene Anwendung, die dann über den lokalen Port sich
die Infos die sie braucht holt und anzeigt.
- die lokale Kommunikation zwischen Server-GUI und Server-Dienst ist natürlich verschlüsselt und die GUI
muss sich natürlich erst beim connecten verifizieren.

^^ Aber das must Du jetzt nicht alles machen, man muss ja auch nicht mit Kanonen auf Spatzen schiessen ;-)

Alles nur Denkanstösse,

Greetz DataCool


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:57 Uhr.
Seite 2 von 3     12 3      

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