Delphi-PRAXiS

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 07:26


indy10 / TCPServer /TCPClient ->Datei versenden
 
hi Leuts

Ich mach grad n kleines Testprogramm. Ich möchte eine Datei vom einen zum anderen Client verschicken, übers Netzwerk mit Progressbaranzeige auf beiden seiten!

Habe leider aber nach langer Suche immer noch nichts dazu gefunden, weder hier im Forum noch auf google. Vielleicht kennt ja jmd von euch ein brauchbares Tutorial!

Liebe Grüsse

Der.Kaktus 3. Apr 2008 07:32

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

hab aber hier was gefunden--->Transfer Indy Fortschrittsanzeige :dp:

cherry 3. Apr 2008 08:16

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Hi Kaktus und Danke vielmals.

Bin den Code soeben am testen, aber ich stelle leider fest, dass wenn ich die variable
Delphi-Quellcode:
IDS: TidStreamVCL;
deklarieren will, der Typ TIdStreamVCL NICHT gefunden wird obwohl ich das
Delphi-Quellcode:
uses IdStreamVCL;
drin habe!

Jmd eine Idee?

STS301 3. Apr 2008 08:24

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
suche einmal nach der Datei "idStreamVCL.pas", wenn es sie auf deinem Rechner nicht gibt, dann weißt du es

oder du hast die Datei nicht in den Suchpfad eingefügt

cherry 3. Apr 2008 08:52

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
daran liegts eben nicht. die Datei ist vorhanden und BDS findet sie auch. in den uses habe ich sie angegeben und dort wird sie auch erkannt! (nicht rot unterstrichen) aber es scheint, als sei dieser Typ (TIdStreamVCL) gar nicht vorhanden dort?!

Die Datei liegt in C:\Programme\BDS\4.0\Indy10\System

Der gesamte Inhalt der Datei:

Delphi-Quellcode:
{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  ([url]http://www.indyproject.org/[/url])

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}

unit IdStreamVCL;

interface
{$I IdCompilerDefines.inc}

uses
  Classes,
  IdGlobal;

type

  TIdStreamHelperVCL = class
  public
    class function ReadBytes(
          const AStream: TStream;
          var VBytes: TIdBytes;
          const ACount: Integer = -1;
          const AOffset: Integer = 0) : Integer; {$IFDEF DOTNET} static; {$ENDIF}
    class procedure Write(
          const AStream: TStream;
          const ABytes: TIdBytes;
          const ACount: Integer = -1); {$IFDEF DOTNET} static; {$ENDIF}
  end;

implementation

class function TIdStreamHelperVCL.ReadBytes(const AStream: TStream; var VBytes: TIdBytes;
  const ACount, AOffset: Integer): Integer;
var
 aActual:Integer;
begin
  Assert(AStream<>nil);
  Result:=0;

  if VBytes = nil then
  begin
    SetLength(VBytes, 0);
  end;
  //check that offset<length(buffer)? offset+count?
  //is there a need for this to be called with an offset into a nil buffer?

  aActual:=ACount;
  if aActual = -1 then begin
    aActual := AStream.Size - AStream.Position;
  end;

  //this prevents eg reading 0 bytes at Offset=10 from allocating memory
  if aActual=0 then begin
    Exit;
  end;

  if Length(VBytes) < (AOffset+aActual) then begin
    SetLength(VBytes, AOffset+aActual);
  end;

  Assert(VBytes<>nil);
  Result := AStream.Read(VBytes[AOffset], aActual);
end;

class procedure TIdStreamHelperVCL.Write(const AStream: TStream;const ABytes: TIdBytes;
  const ACount: Integer);
var
 aActual:Integer;
begin
  Assert(AStream<>nil);

  aActual:=ACount;
  //should we raise assert instead of this nil check?
  if ABytes <> nil then
  begin
    if aActual = -1 then
    begin
      aActual := Length(ABytes);
    end
    else
    begin
      aActual := Min(aActual, Length(ABytes));
    end;
    if aActual > 0 then
    begin
      AStream.Write(ABytes[0], aActual);
    end;
  end;
end;

end.
Da seh ich nichts von TIdStreamVCL!

Wenn ich diesen Typ: TIdStreamHelperVCL verwende, kann ich im Konstruktor aber keinen TFileStream angeben!

so sollte es sein:
Delphi-Quellcode:
TidStreamVCL.Create(FStream);
das kann ich nur:
Delphi-Quellcode:
TidStreamHelperVCL.Create
aber das bringt mir nichts!

Ideeen?

STS301 3. Apr 2008 09:50

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
füge einmal "IdGlobal" in die Uses Liste ein ;)

cherry 3. Apr 2008 09:51

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
das hab ich auch schon versucht. Leider kein Erfolg!

STS301 3. Apr 2008 09:57

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
@TidStreamVCL

es ist ja keine visuelle Komponente, daher kannst du davon auch nichts sehen (da auch kein Registryeintrag da ist kann es auch unmöglich eine Komponente sein)

cherry 3. Apr 2008 10:00

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Hä? Wie meinste das jetz?! mir ist bewusst das dies keine visuelle Komponente ist. Tatsache ist aber, dass es den Typ (TidStreamVCL) nicht gibt bei mir?!

Fehler beim versuch das Programm zu compilieren:

Delphi-Quellcode:
[Pascal Fehler] main.pas(39): E2003 Undefinierter Bezeichner: 'TidStreamVCL'

STS301 3. Apr 2008 10:02

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
schau dir einmal den Thread Hier im Forum suchenidstreamvcl Daten via tcp indy 10 verschicken an, das hilt dir bestimmt ;)

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

ManuMF 7. Apr 2008 13:59

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Ich weiß nicht, ob es in diesem Fall nützlich ist, aber vor einiger Zeit habe ich mich mal ans Senden per TCP und Indy 10 gemacht, und dazu auch was für die CodeLib aufbereitet. Du findest den Code hier, vielleicht hilft dir ein Teil daraus (Stichwort Stream) weiter :)

cherry 7. Apr 2008 14:04

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Danke für deine schnelle Antwort...
... und wieder sind mir einige Lichter aufgegangen... habe aber leider immer noch div. Probleme...

Also, was ich will ist erstmal folgendes...

- Programm#1 will Programm#2 eine Datei schicken. Und sendet (cmd 1) an Prg#2
- Programm#2 erhält den Command 1 (jmd will datei senden)
- Programm#2 akzeptiert (cmd 2) oder lehnt Datei ab (cmd 3)
- Programm#1 wenn Prg#1 (cmd 2) erhält versucht es Datei an Prg#2 zu senden. Wenn (cmd 3) dann abbrechen.

Dabei ist Programm#1 und Programm#2 genau dasselbe... läuft einfach auf unterschiedlichen Rechnern...
Die Programm sollen die Informationen so austauschen, dass das andere immer weiss was das andere macht.

Wenn ich das jetzt so mache wie du mir vorschlägst:

Delphi-Quellcode:
.. 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;
  // ...
Dann überschreibts ja immer gleich tmpClient.LastCmd...
Wie kann ich nun z.B. den Namen der Datei schicken?! usw.

Es müsste ja irgendwie möglich sein zuerst einen befehl zu schicken und dann noch weitere Parameter z.b. Filename...
oder dann beim uploaden den befehl z.b (cmd 2) für upload und dann den stream...

Irgendwo hab ich noch ne grosse lücke!

Gruss

cherry 9. Apr 2008 06:13

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Allen vielen Dank für eure Hilfe...

Fürs erste hab ichs nun hinbekommen, scheint jedenfalls zu funktionieren. Eine kleine Frage hätte ich da aber noch, wie kann man nun die Progressbar des "uploaders" (TCPServer) anzeigen lassen?

hier mein aktueller code:

Delphi-Quellcode:
// SEND FILE / ACCEPT INCOMMING FILE (same button)
procedure TForm1.EButton2Click(Sender: TObject);
begin
  if EButton2.Caption = 'upload' then
  begin
    if OpenDialog1.Execute then
    begin
      try
        uploadfilepath := OpenDialog1.FileName;
        EButton2.Enabled := False;
        EButton5.Enabled := False;
        TCPClient.Connect;
        // upload file
        TCPSendCmd(CmdUploadFile);
        TCPClient.IOHandler.WriteLn(ExtractName(uploadfilepath));
        Log(msg001);
        Log('wait for client reply...');
      except
        on e:Exception do
        begin
          Log(e.Message);
          EButton2.Enabled := True;
        end;
      end;
    end;
  end
  else
  begin
    try
      ButtonCaption('receive');
      EButton2.Enabled := False;
      EButton4.Enabled := False;
      EButton5.Enabled := False;
      TCPClient.Connect;
      // accept file
      TCPSendCmd(CmdRecFileOK);
    except
      on e:Exception do
        Log(e.Message);
    end;
  end;
end;
und

Delphi-Quellcode:
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
  FSTream: TFileStream;
  df: String;
  tmpClientData : TClientData;
begin

  if not assigned(AContext.Data) then
  begin
    AContext.Connection.Disconnect;
    exit;
  end;

  // read clientdata
  try
    tmpClientData := TClientData(AContext.Data);
  except
    AContext.Connection.Disconnect;
    exit;
  end;

  // check state
  if (tmpClientData.Status = cCSGoing2Disconnect) or
     (tmpClientData.Status = cCSDisconnected) then
  begin
    AContext.Connection.Disconnect;
    exit;
  end;

  // get command
  try
    tmpClientData.LastCmd := AContext.Connection.Socket.ReadLn(#$A,5000,1024);
  except
    tmpClientData.LastCmd := '';
  end;

  // check the old command
  case tmpClientData.LastIntCmd of
    // GUY WANTS TO SEND FILE
    1:
    begin
      savefilepath := DestinationEdit.Text + AContext.Connection.IOHandler.ReadLn;
      Log(AContext.Binding.PeerIP+' wants to send file: "'+ExtractName(savefilepath)+'"');
      ButtonCaption('receive');
      EButton4.Enabled := True;
    end;
    // UPLOAD FILE
    2:
    begin
      Log('upload file...');
      TCPSendCmd(4);
      try
        // upload
        FStream := TFileStream.Create(uploadfilepath, fmOpenRead or fmShareDenyWrite);
        TCPClient.IOHandler.Write(FStream,0,true);
        // disconnect
        AContext.Connection.Disconnect;
        if TCPClient.Connected then
          TCPClient.Disconnect;
        tmpClientData.Status := cCSDisconnected;
      finally
        FStream.Free;
      end;
      Log('successfully uploaded');
      ButtonCaption('upload');
      EButton4.Enabled := False;
      EButton2.Enabled := True;
      EButton5.Enabled := True;
    end;
    // GUY REFUSED FILE
    3:
    begin
      Log('guy refused to download file');
      // disconnect
      AContext.Connection.Disconnect;
      if TCPClient.Connected then
        TCPClient.Disconnect;
      tmpClientData.Status := cCSDisconnected;
      ButtonCaption('upload');
      EButton4.Enabled := False;
      EButton2.Enabled := True;
      EButton5.Enabled := True;
    end;
    // DOWNLOAD FILE
    4:
    begin
      try
        if FileExists(savefilepath) then
        begin
          Log('file was already existing');
          df := savefilepath;
          DeleteFile(df);
          Log('old file deleted!');
        end
        else if DirectoryExists(ExtractFilePath(savefilepath)) = false then
        begin
          CreateDir(ExtractFilePath(savefilepath));
          Log('directory "'+ExtractFilePath(savefilepath)+'" created');
        end;
        Log('download...');
        try
          // download
          FStream := TFileStream.Create(savefilepath, fmCreate);
          AContext.connection.IOHandler.ReadStream(fstream);
          // disconnect
          AContext.Connection.Disconnect;
          if TCPClient.Connected then
            TCPClient.Disconnect;
          tmpClientData.Status := cCSDisconnected;
        finally
          FStream.Free;
        end;
        Log('file successfully downloaded!');
        ButtonCaption('upload');
        EButton4.Enabled := False;
        EButton2.Enabled := True;
        EButton5.Enabled := True;
      except
        on e:Exception do
          Log(e.Message);
      end;
    end;
    // RECEIVE CHAT MESSAGE
    5:
    begin
      Log(AContext.Binding.PeerIP+': '+AContext.Connection.IOHandler.ReadLn);
      AContext.Connection.Disconnect;
    end;
  end;
end;
hehe

Delphi-Quellcode:
procedure TForm1.TCPSendCmd(Cmd: Byte);
begin
  try
    TCPClient.IOHandler.WriteLn(IntToStr(Cmd));
  except
    on e:Exception do
      Log(e.Message);
  end;
end;

mleyen 18. Apr 2008 07:53

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Hi,
so jetzt frage ich doch nochmal...

Was ich will:
Ich will ein TJpegImage oder TBitmap in einen Stream speichern und dann den Stream über TCPClient an den TCPServer senden. (Höhrt sich einfach an, treibt mich aber noch in den Wahnsinn)
Das schlimme dabei ist, ich benutze Indy 10 und da gibt es keinen 'WriteStream'! :roll:

Wie ich es bisher umgangen hab:
Ich hab das JPG in eine Datei gespeichert und die Datei dann versendet, empfangen, geladen, gelöscht und das funktioniert auch perfekt nur ist es halt regelrecht lahm aufgrund der vielen Festplattenzugriffe.

Bisher hab ich folgendes:
Delphi-Quellcode:
// Client sendet den Stream
memStream := TMemoryStream.Create;
jpgTmp.SaveToStream(memStream);
memStream.Position := 0;
FClient.IOHandler.Write(memStream);

// Server epfängt den Stream
memStream := TMemoryStream.Create;
AContext.Connection.IOHandler.ReadStream(memStream,FileSize);
memStream.Position := 0;
jpgTmp.LoadFromStream(memStream);
Bei der Filesize weiß ich, das sie den richtigen Wert enthält.
Wenn ich aber das Debug hängt der Client in 'Write' und der server in 'ReadStream' und weiter gehts nicht mehr. :gruebel:
Oder habe ich eventuell jetzt schon einen Fehler gemacht? :pale:


Zitat:

Zitat von DataCool
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.

Das ist einer der wenigen Sachen die ich noch nicht ausprobiert habe, da ich nicht weiß wie ich den LStream 'Createn' soll, da als Parameter immer ein Dateipfad benötigt wird anstatt einen Stream.


Ich hoffe irgendwer hat eine Lösung parat bzw kann mir helfen, dass ich den Stream versendet und empfangen bekomm.
Danke schonmal!

STS301 18. Apr 2008 07:56

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
schau dir bitte den Beitrag von ManuMF #21 an, das wird bestimmt helfen

mleyen 18. Apr 2008 08:10

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

Zitat von STS301
schau dir bitte den Beitrag von ManuMF #21 an, das wird bestimmt helfen

Leider nicht, hab ich auch schon ausprobiert.

Die Unit 'IdStreamVCL' enthält bei mir keinen Typ 'TidStreamVCL' sondern nur 'TIdStreamHelperVCL'. (Welcher nichtmal einen Parameter im Create hat)

STS301 18. Apr 2008 08:34

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
schau dir den Beitrag #10 an

mleyen 18. Apr 2008 08:58

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
Mit dem Post wolltest du wahrscheinlich auf diesen Thread hinweisen.
Das ist aber genau der murks den ich momentan mache. (wie bereits erwähnt)
Da werden keine Daten direkt versendet per Stream, da wird erst in eine Datei gespeichert, die Datei wird versendet und beim Empfänger gespeichert, Beim empfänger wird die Datei geladen und dann erst hat man den Stream/Inhalt.

Das ganze hin und hergespeichere kann man sich doch bestimmt ersparen indem man direkt einen Stream versendet, nur wie? :cry:

Naja ich hau jetzt Indy 10 raus und Indy 9 rein.
Dann hab ich auch mein 'WriteStream' und die Welt ist heile... :thumb: (hoffentlich^^)

Danke trotzdem!

STS301 18. Apr 2008 09:43

Re: indy10 / TCPServer /TCPClient ->Datei versenden
 
für Indy 9 wirst du im Delphitreff forum viel finden

DataCool 18. Apr 2008 13:21

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

für Indy9 habe ich ein komplettes Bsp. von mir fertig :

Beam me up

Greetz DataCool


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:12 Uhr.

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