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
Der Horizont vieler Menschen ist ein Kreis mit Radius Null, und das nennen sie ihren Standpunkt.