Discussion:
Problem sending file with IdTCPClient, IdTCPServer (TFileStream + Indy sockets) Please advise!
(too old to reply)
Nicke
2003-11-18 17:08:47 UTC
Permalink
Hi!

I'm fairly new to Delphi programming.. anyway my problem is that when i send
a file through the client -> server utlilising the Indy component my file
end up beeing ZERO size. I will include my unit1.pas file here so
you can take a look on what's wrong.

Thanks.

Nicke
unit1.pas
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs,
Menus, CoolTrayIcon, IdBaseComponent, IdComponent, IdTCPServer, StdCtrls,
Sockets, TypInfo, ComCtrls, Registry, XPMan, ExtCtrls, IdTCPConnection,
IdTCPClient;

type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
TrayIcon1: TCoolTrayIcon;
PopupMenu1: TPopupMenu;
Showwindow1: TMenuItem;
Exit1: TMenuItem;
About1: TMenuItem;
MainMenu1: TMainMenu;
File1: TMenuItem;
About2: TMenuItem;
Exit2: TMenuItem;
XPManifest1: TXPManifest;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
GroupBox1: TGroupBox;
Memo1: TMemo;
GroupBox2: TGroupBox;
CheckBox1: TCheckBox;
Label1: TLabel;
CheckBox2: TCheckBox;
LabeledEdit1: TLabeledEdit;
Label2: TLabel;
TabSheet3: TTabSheet;
OpenDialog1: TOpenDialog;
StatusBar1: TStatusBar;
GroupBox3: TGroupBox;
Button1: TButton;
Edit1: TEdit;
Label3: TLabel;
Label4: TLabel;
Button2: TButton;
Label6: TLabel;
GroupBox4: TGroupBox;
LabeledEdit2: TLabeledEdit;
Button3: TButton;
ProgressBar1: TProgressBar;
Label7: TLabel;
IdTCPClient1: TIdTCPClient;
Button4: TButton;
Label5: TLabel;
LabeledEdit3: TLabeledEdit;
SaveDialog1: TSaveDialog;

procedure TrayIcon1Startup(Sender: TObject; var ShowMainForm: Boolean);
procedure TrayIcon1MinimizeToTray(Sender: TObject);
procedure ShowWindow1Click(Sender: TObject);
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
procedure IdTCPServer1Connect(AThread: TIdPeerThread);
procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
procedure Exit1Click(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure About2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
function LoadSetting(Key, Item, RegType: String; DefValue: Variant):
Variant;
procedure SaveSetting(Key, Item, RegType: String; Value: Variant);
procedure CheckBox1Click(Sender: TObject);
procedure LoadRegSettings();
procedure SetNetworkConfig();
procedure FormCreate(Sender: TObject);
procedure CheckBox2Click(Sender: TObject);
procedure LabeledEdit1Change(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure PageControl1Change(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure LabeledEdit2Change(Sender: TObject);
procedure LabeledEdit3Change(Sender: TObject);
procedure Button2Click(Sender: TObject);

private
{ Private declarations }

public
{ Public declarations }

end;

var
Form1: TForm1;

implementation

{$R *.dfm}

type serverCommands = (QUIT, SEND, SHUTDOWN, PING);

const
ASERV_MAJOR_VERSION = 'BETA 01';
serverMsg = 'Message from server: ';
REGKEY_SETTINGS = 'Software\aServ\Settings';

var
SysTrayConnectionNotify : Boolean;
StartMinimized : Boolean;
ListenOnPort : Integer;
RemoteHost : String;
RemoteHostPort : Integer;
S : TMemoryStream;
FileName : String;
FStream : TFileStream;

procedure TForm1.SetNetworkConfig();
begin
//set the default port and interface to listen on
IdTCPServer1.Bindings.Add.IP := '0.0.0.0';
IdTCPServer1.Bindings.Add.Port := ListenOnPort;

//activate the server
IdTCPServer1.Active := True;
end;


procedure TForm1.LoadRegSettings();
begin
Application.ProcessMessages;

//what remote host should we connect to
RemoteHost := LoadSetting(REGKEY_SETTINGS, 'RemoteHost', 'String',
'localhost');
LabeledEdit2.Text := RemoteHost;

//what remote port should we connect to
RemoteHostPort := LoadSetting(REGKEY_SETTINGS, 'RemoteHostPort', 'String',
'8000');
LabeledEdit3.Text := IntToStr(RemoteHostPort);

//should we be notified about connection events when minimized to systray
SysTrayConnectionNotify := LoadSetting(REGKEY_SETTINGS,
'SysTrayConnectionNotify', 'Boolean', False);
CheckBox1.Checked := SysTrayConnectionNotify;

//should we start minimized to systray
ListenOnPort := LoadSetting(REGKEY_SETTINGS, 'ListenOnPort', 'String',
'8000');
LabeledEdit1.Text := IntToStr(ListenOnPort);

//what port should we listen on
StartMinimized := LoadSetting(REGKEY_SETTINGS, 'StartMinimized',
'Boolean', False);
CheckBox2.Checked := StartMinimized;

Label1.Caption := 'Settings reloaded!';
Label1.Update;
Sleep(50);
Label1.Caption := ' ';
Label1.Update;
end;


function TForm1.LoadSetting(Key, Item, RegType: String; DefValue: Variant):
Variant;
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(Key);
if RegType = 'String' then
Result := Reg.ReadString('', Item, DefValue)
else if RegType = 'Boolean' then
Result := Reg.ReadBool('', Item, DefValue);
Reg.Free;
end;


procedure TForm1.SaveSetting(Key, Item, RegType: String; Value: Variant);
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(Key);
if RegType = 'String' then
Reg.WriteString('', Item, Value)
else if RegType = 'Boolean' then
Reg.WriteBool('', Item, Value);
Reg.Free;
end;


procedure TForm1.TrayIcon1Startup(Sender: TObject; var ShowMainForm:
Boolean);
begin

//load our settings from the registry
LoadRegSettings();

if StartMinimized then
begin
//show our main form
ShowMainForm := False;
//hide the systray icon
TrayIcon1.IconVisible := True;
end
else
begin
//hide our main form (start in systray)
ShowMainForm := True;
//show the systray icon
TrayIcon1.IconVisible := False;
//load our settings from the registry
end;
end;


procedure TForm1.ShowWindow1Click(Sender: TObject);
begin
//hide the systray icon when we acticvate the main window
TrayIcon1.IconVisible :=False;
//show our main window
TrayIcon1.ShowMainForm; // ALWAYS use this method!!!
end;


procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
sCmd : String;

begin
FStream := TFileStream.Create('C:\' + ExtractFileName(FileName), fmCreate
or fmShareExclusive);

with AThread.Connection do
try
memo1.SelText := 'start write' + #13#10;
ReadStream(FStream, -1, False);
memo1.SelText := 'start write second' + #13#10;
finally
memo1.SelText := 'end write' + #13#10;
IdTCPClient1.Disconnect;
FStream.Free;
memo1.SelText := 'disconnect write' + #13#10;
end;
end;


procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
//display a messsage when a client has connected
memo1.SelText := 'Client connected: ' + FormatDateTime('hh:mm:ss', now) +
' [IP: ' + AThread.Connection.Socket.Binding.PeerIP +']' + #13#10;
if SysTrayConnectionNotify then
TrayIcon1.ShowBalloonHint('Connection event', 'Client connected from: '
+ AThread.Connection.Socket.Binding.PeerIP, bitInfo, 10);

//greeting message
AThread.Connection.WriteLn('Greetings!');
end;


procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
//display a messsage when a client has disconnected
memo1.SelText := 'Client disconnected: ' + FormatDateTime('hh:mm:ss', now)
+ #13#10;
end;


procedure TForm1.Exit1Click(Sender: TObject);
begin
//exit our application
Application.Terminate;
end;


procedure TForm1.About1Click(Sender: TObject);
begin
//TrayIcon1.ShowBalloonHint('aServ' + ASERV_MAJOR_VERSION , 'Please visit
http://www.nicke.nu', bitInfo, 10);
if SysTrayConnectionNotify then
TrayIcon1.ShowBalloonHint('aServ ' + ASERV_MAJOR_VERSION, 'Förra året
fick Aftonbladets läsare dela på en miljon kronor för att sätta guldkant på
tillvaron. Gensvaret var enormt och succén omedelbar.' + 'Över en halv
miljon ansökningar kom in där människor önskade sig allt från nya skolböcker
till prinsesskalas. 130 personer fick slutligen sin önskan uppfylld.',
bitInfo, 10);
end;


procedure TForm1.About2Click(Sender: TObject);
begin
//display an about box
ShowMessage('aServ ' + ASERV_MAJOR_VERSION + #13#10 + 'Copyright 2003
nicke.nu');
end;


procedure TForm1.TrayIcon1MinimizeToTray(Sender: TObject);
begin
//don't show the form in the taskbar
TrayIcon1.HideTaskbarIcon;
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
//setup networking stuff
SetNetworkConfig();

if not Memo1.SelLength <= 0 then
Memo1.Text := ' ';
end;


procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
begin
SaveSetting(REGKEY_SETTINGS, 'SysTrayConnectionNotify', 'Boolean',
True);
end
else
begin
SaveSetting(REGKEY_SETTINGS, 'SysTrayConnectionNotify', 'Boolean',
False);
end;
//load our settings from the registry
LoadRegSettings();
end;


procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox2.Checked then
begin
SaveSetting(REGKEY_SETTINGS, 'StartMinimized', 'Boolean', True);
end
else
begin
SaveSetting(REGKEY_SETTINGS, 'StartMinimized', 'Boolean', False);
end;

//load our settings from the registry
LoadRegSettings();
end;

procedure TForm1.LabeledEdit1Change(Sender: TObject);
begin
if LabeledEdit1.Text = '0' then
else if LabeledEdit1.Text = '' then
else
SaveSetting(REGKEY_SETTINGS, 'ListenOnPort', 'String',
LabeledEdit1.Text);
end;


procedure TForm1.Button1Click(Sender: TObject);

var FSizeKB : Double;
var FSizeMB : Double;

begin
if OpenDialog1.Execute then
begin
FileName := OpenDialog1.FileName;
S := TMemoryStream.Create;

try
if FileExists(FileName) then
begin
//display the 'File size' label
Label3.Visible := True;

//set the decimal seperator character
DecimalSeparator := '.';

//load our file
S.LoadFromFile(FileName);

//setup file size formats
FSizeKB := S.Size / 1024;
FSizeMB := FSizeKB / 1024;

//if file size is smaller then one KB display in bytes
if S.Size <= 1024 then
Label4.Caption := IntToStr(S.Size) + ' bytes'
// if file size is bigger then one KB but smaller then one MB
display in KB
// else display in MB
else
if FSizeKB >= 1024 then
Label4.Caption := FormatFloat('#.00', FSizeMB) + ' MB ' +
'(' + FormatFloat('0', FSizeKB) + ' KB)'
else
Label4.Caption := FormatFloat('0', FSizeKB) + ' KB';

Edit1.Text := FileName;
StatusBar1.Panels[0].Text := 'Filename: ' + FileName;
end
else
//if file not found
MessageDlg('File not found! Try again.', mtError , [mbOK], 0);
finally
S.Free;
end;
end;
end;


procedure TForm1.PageControl1Change(Sender: TObject);
begin
StatusBar1.Panels[0].Text := ' ';
end;


procedure TForm1.Button3Click(Sender: TObject);
begin
try
if LabeledEdit2.Text = '' then
MessageDlg('You must enter a host to connect to.', mtError , [mbOK],
0)
else if LabeledEdit3.Text = '' then
MessageDlg('You must enter a port.', mtError , [mbOK], 0)
else
begin
if not IdTCPClient1.Connected then
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);

IdTCPClient1.Connect;
Label5.Caption := 'Connected';
end;
end;
finally
end;
end;


procedure TForm1.Button4Click(Sender: TObject);
begin
if IdTCPClient1.Connected then
begin
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
end;


procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
//disconnect our client connections if any
if IdTCPClient1.Connected then
IdTCPClient1.Disconnect;
end;

procedure TForm1.LabeledEdit2Change(Sender: TObject);
begin
SaveSetting(REGKEY_SETTINGS, 'RemoteHost', 'String', LabeledEdit2.Text);
end;

procedure TForm1.LabeledEdit3Change(Sender: TObject);
begin
SaveSetting(REGKEY_SETTINGS, 'RemoteHostPort', 'String',
LabeledEdit3.Text);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
IdTCPClient1.Connect;

FStream := TFileStream.Create(FileName, fmOpenRead);

with IdTCPClient1 do
try
IdTCPClient1.OpenWriteBuffer;
try
IdTCPClient1.WriteStream(FStream, True, True, 0);
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
//IdTCPClient1.Disconnect;
FStream.Free;
end;
end;
end.

<<<<<< unit1.pas
Remy Lebeau (TeamB)
2003-11-18 19:10:10 UTC
Permalink
Post by Nicke
I will include my unit1.pas file here
Please do not do that. Never include that much code unless someone actually
asks for it. That was way too much code that had absolutely nothing to do
with your question at all. Please post only the most relavant portions of
the code, namely the socket code in this particular case. Whenever
possible, try to make a completely separate project with minimal code that
reproduces the same problem, and then post that instead. It makes it a lot
easier for people to diagnose your problems better. This applies to all of
Borland's newsgroups in general. Please refer to the Newsgroup Guidelines
at http://www.borland.com/newsgroups.


Gambit
Nicke
2003-11-18 19:28:33 UTC
Permalink
Allright, I'll post a snip of the code instead.

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
I will include my unit1.pas file here
Please do not do that. Never include that much code unless someone actually
asks for it. That was way too much code that had absolutely nothing to do
with your question at all. Please post only the most relavant portions of
the code, namely the socket code in this particular case. Whenever
possible, try to make a completely separate project with minimal code that
reproduces the same problem, and then post that instead. It makes it a lot
easier for people to diagnose your problems better. This applies to all of
Borland's newsgroups in general. Please refer to the Newsgroup Guidelines
at http://www.borland.com/newsgroups.
Gambit
Nicke
2003-11-18 21:42:45 UTC
Permalink
OK, here's the two procedures mainly involved. Have I done it corecctly or
could you do it better in some other way using this or another method?

I have some questions regarding the data transfer. It seems like the server
proc will create the file on the disk (having the full size) before the
entire data stream is transfered. How come?

Also where can I find any useful information or tutorial regarding Indy
Client<->Server communication and data exchanging.
Post by Nicke
Post by Remy Lebeau (TeamB)
Code
procedure TForm1.Button2Click(Sender: TObject);
var
FStream : TFileStream;

begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
IdTCPClient1.Connect;

FStream := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);

with IdTCPClient1 do
try
IdTCPClient1.OpenWriteBuffer;
try
memo1.SelText := 'begin to write the stream' + #13#10;
IdTCPClient1.WriteStream(FStream, True, True, 0);
memo1.SelText := 'wrote the stream' + #13#10;
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
//should not be here?
//IdTCPClient1.Disconnect;
//FStream.Free;
end;
end;


procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
sCmd : String;

begin
//FStream := TFileStream.Create('C:\' + ExtractFileName(FileName),
fmCreate);
FStream := TFileStream.Create('C:\test.fil', fmOpenWrite or fmCreate);

with AThread.Connection do
try
memo1.SelText := 'start write' + #13#10;
ReadStream(FStream, -1, False);
memo1.SelText := 'start write second' + #13#10;
finally
memo1.SelText := 'end write' + #13#10;
AThread.Connection.Disconnect;
IdTCPClient1.Disconnect;
FStream.Free;
memo1.SelText := 'disconnect write' + #13#10;
end;
end;

<< Code
Post by Nicke
Allright, I'll post a snip of the code instead.
Thanks.
/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
I will include my unit1.pas file here
Please do not do that. Never include that much code unless someone
actually
Post by Remy Lebeau (TeamB)
asks for it. That was way too much code that had absolutely nothing to do
with your question at all. Please post only the most relavant portions of
the code, namely the socket code in this particular case. Whenever
possible, try to make a completely separate project with minimal code that
reproduces the same problem, and then post that instead. It makes it a
lot
Post by Remy Lebeau (TeamB)
easier for people to diagnose your problems better. This applies to all
of
Post by Remy Lebeau (TeamB)
Borland's newsgroups in general. Please refer to the Newsgroup Guidelines
at http://www.borland.com/newsgroups.
Gambit
Remy Lebeau (TeamB)
2003-11-18 23:23:24 UTC
Permalink
It seems like the server proc will create the file on the disk (having
the full size) before the entire data stream is transfered. How come?
It is going to create the file regardless, so it has something to store to.
However, with the way you have the parameters set up, the stream is
transmitting the file's size to the server as well as the file data, so the
server knows ahead of time how large the file is going to be and can
pre-allocate the full storage for it. That will then speed up the file I/O
on the OS's part.
Also where can I find any useful information or tutorial regarding
Indy Client<->Server communication and data exchanging.
Go to Indy's website and download the documentation, articles, and demos.
FStream := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
I do not suggest you open the file for exclusive access. The only thing you
really need to make sure of is that noone modifies the file while you are
sending it, but it shouldn't matter whether anyone reads the files only:

FStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
IdTCPClient1.OpenWriteBuffer;
Be careful with using write buffering, especially when you are not using any
kind of threshold value. Without that, you're going to end up putting the
entire file into memory before sending it. For small files, that is not
bad. But for large files, it will be. Either don't use write buffering at
all, or at least specify a threshold so that the buffer can be flushed to
the socket and cleared from memory periodically.
finally
//should not be here?
//IdTCPClient1.Disconnect;
//FStream.Free;
end;
Yes, you should be in there, that is what the 'finally' is for - code that
is always executed whether an exception is thrown or not. And yes, you do
need to clean up after yourself at that point, so remove the comments and
make the code active again. You need to close the socket when you're done
with it (unless you want to leave it open for doing other things). But you
do definately need to free the stream, otherwise its memory will be leaked.
FStream := TFileStream.Create('C:\test.fil', fmOpenWrite or fmCreate);
Specify any other flags with fmCreate is redundant, as fmCreate already
includes all flags.
memo1.SelText := 'start write' + #13#10;
The OnExecute event is triggered in the context of a worker thread. You
cannot safely access the GUI directly, unless you use the thread's
Synchronize() method.
IdTCPClient1.Disconnect;
I do not recommand you do that from the server's OnExecute event. Let the
client handle its own disconnecting from its own code. Leave the server
code to perform server-only operations.

Is there any particular reason why you have the server and client in the
same application talking to each other? If it is just for test purposes, I
can understand that, but if you really want to learn efficient socket
programming, you should make separent client and server applications.


Gambit
Nicke
2003-11-19 10:36:32 UTC
Permalink
Thanks a bunch! Great answes. Do you mean that 3000 pages PDF document (Indy
doc)?

Yes this is for test purposes only. I'll be posting here when I have some
more questions.

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
It seems like the server proc will create the file on the disk (having
the full size) before the entire data stream is transfered. How come?
It is going to create the file regardless, so it has something to store to.
However, with the way you have the parameters set up, the stream is
transmitting the file's size to the server as well as the file data, so the
server knows ahead of time how large the file is going to be and can
pre-allocate the full storage for it. That will then speed up the file I/O
on the OS's part.
Also where can I find any useful information or tutorial regarding
Indy Client<->Server communication and data exchanging.
Go to Indy's website and download the documentation, articles, and demos.
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareExclusive);
Post by Remy Lebeau (TeamB)
I do not suggest you open the file for exclusive access. The only thing you
really need to make sure of is that noone modifies the file while you are
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
Post by Remy Lebeau (TeamB)
IdTCPClient1.OpenWriteBuffer;
Be careful with using write buffering, especially when you are not using any
kind of threshold value. Without that, you're going to end up putting the
entire file into memory before sending it. For small files, that is not
bad. But for large files, it will be. Either don't use write buffering at
all, or at least specify a threshold so that the buffer can be flushed to
the socket and cleared from memory periodically.
finally
//should not be here?
//IdTCPClient1.Disconnect;
//FStream.Free;
end;
Yes, you should be in there, that is what the 'finally' is for - code that
is always executed whether an exception is thrown or not. And yes, you do
need to clean up after yourself at that point, so remove the comments and
make the code active again. You need to close the socket when you're done
with it (unless you want to leave it open for doing other things). But you
do definately need to free the stream, otherwise its memory will be leaked.
FStream := TFileStream.Create('C:\test.fil', fmOpenWrite or fmCreate);
Specify any other flags with fmCreate is redundant, as fmCreate already
includes all flags.
memo1.SelText := 'start write' + #13#10;
The OnExecute event is triggered in the context of a worker thread. You
cannot safely access the GUI directly, unless you use the thread's
Synchronize() method.
IdTCPClient1.Disconnect;
I do not recommand you do that from the server's OnExecute event. Let the
client handle its own disconnecting from its own code. Leave the server
code to perform server-only operations.
Is there any particular reason why you have the server and client in the
same application talking to each other? If it is just for test purposes, I
can understand that, but if you really want to learn efficient socket
programming, you should make separent client and server applications.
Gambit
Nicke
2003-11-19 10:46:39 UTC
Permalink
Forgot to ask one thing. How big should the writebuffer (threshold) be?
What's a reasonable size here?

/Nicke
Post by Remy Lebeau (TeamB)
It seems like the server proc will create the file on the disk (having
the full size) before the entire data stream is transfered. How come?
It is going to create the file regardless, so it has something to store to.
However, with the way you have the parameters set up, the stream is
transmitting the file's size to the server as well as the file data, so the
server knows ahead of time how large the file is going to be and can
pre-allocate the full storage for it. That will then speed up the file I/O
on the OS's part.
Also where can I find any useful information or tutorial regarding
Indy Client<->Server communication and data exchanging.
Go to Indy's website and download the documentation, articles, and demos.
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareExclusive);
Post by Remy Lebeau (TeamB)
I do not suggest you open the file for exclusive access. The only thing you
really need to make sure of is that noone modifies the file while you are
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
Post by Remy Lebeau (TeamB)
IdTCPClient1.OpenWriteBuffer;
Be careful with using write buffering, especially when you are not using any
kind of threshold value. Without that, you're going to end up putting the
entire file into memory before sending it. For small files, that is not
bad. But for large files, it will be. Either don't use write buffering at
all, or at least specify a threshold so that the buffer can be flushed to
the socket and cleared from memory periodically.
finally
//should not be here?
//IdTCPClient1.Disconnect;
//FStream.Free;
end;
Yes, you should be in there, that is what the 'finally' is for - code that
is always executed whether an exception is thrown or not. And yes, you do
need to clean up after yourself at that point, so remove the comments and
make the code active again. You need to close the socket when you're done
with it (unless you want to leave it open for doing other things). But you
do definately need to free the stream, otherwise its memory will be leaked.
FStream := TFileStream.Create('C:\test.fil', fmOpenWrite or fmCreate);
Specify any other flags with fmCreate is redundant, as fmCreate already
includes all flags.
memo1.SelText := 'start write' + #13#10;
The OnExecute event is triggered in the context of a worker thread. You
cannot safely access the GUI directly, unless you use the thread's
Synchronize() method.
IdTCPClient1.Disconnect;
I do not recommand you do that from the server's OnExecute event. Let the
client handle its own disconnecting from its own code. Leave the server
code to perform server-only operations.
Is there any particular reason why you have the server and client in the
same application talking to each other? If it is just for test purposes, I
can understand that, but if you really want to learn efficient socket
programming, you should make separent client and server applications.
Gambit
Remy Lebeau (TeamB)
2003-11-19 18:22:26 UTC
Permalink
Post by Nicke
Forgot to ask one thing. How big should the writebuffer
(threshold) be? What's a reasonable size here?
Personally, I never use write buffering at all, so I can't answer that.
You'll just have to experiment to see what values work well for your
particular situations.


Gambit
Nicke
2003-11-19 18:47:40 UTC
Permalink
Allrigh, I will do that. Did you see my other post today with the new code?

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Forgot to ask one thing. How big should the writebuffer
(threshold) be? What's a reasonable size here?
Personally, I never use write buffering at all, so I can't answer that.
You'll just have to experiment to see what values work well for your
particular situations.
Gambit
Remy Lebeau (TeamB)
2003-11-19 20:06:02 UTC
Permalink
Post by Nicke
Allrigh, I will do that. Did you see my other post today with the new code?
Yes, and it wll have to wait until later tonight before I can get into it.


Gambit
Nicke
2003-11-20 19:31:45 UTC
Permalink
Have you came up with anything regarding this yet?

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Allrigh, I will do that. Did you see my other post today with the new
code?
Yes, and it wll have to wait until later tonight before I can get into it.
Gambit
Nicke
2003-11-20 19:27:53 UTC
Permalink
Have you came up with anything regarding this yet?

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Allrigh, I will do that. Did you see my other post today with the new
code?
Yes, and it wll have to wait until later tonight before I can get into it.
Gambit
Remy Lebeau (TeamB)
2003-11-20 20:27:26 UTC
Permalink
Post by Nicke
Have you came up with anything regarding this yet?
Sorry, I did not get a chance to look at it last night afterall.
Post by Nicke
WriteStreamBuffer := 5000000; //set a write buffer
Unless your file is greater than 4.7 Mb, that threshold will not be hit.
From your code, it seems that you think the thresholds are a fixed buffer
size. They are not. Buffering is a completely different thing. During the
sending, if you are using write buffering, then the data will be stored in
an internal buffer, and then every 5000000 bytes, the buffer will be flushed
to the socket. If you're going to use write buffer (which again, is
generally not needed to begin with), then I would suggest you use a smaller
threshold value.

IdTCPClient1.Connect;

The fact that you are not specifying any timeout parameter to Connect
suggests to me that you are using an older version of Indy, since Connect in
Indy 9 requires a timeout parameter. If you are, in fact, still using an
older version (probably v8) then I suggest you upgrade to a newer version
before you continue.
Post by Nicke
ProgressBar1.Max := StreamSize;
Do you have OnWork... event handlers assigned to the TIdTCPClient? If not,
then setting up a progress bar will be useless, since it won't be updated
during the actual transfer, which kind of defeats the purpose of using a
progress bar in the first place.
Post by Nicke
IdTCPClient1.WriteStream(FStream, True, True, 0);
Ok, you are telling the client socket to send a stream as well as its byte
length. I will address that issue further down in the server code. Keep
reading...
Post by Nicke
except
IdTCPClient1.CancelWriteBuffer;
IdTCPClient1.Disconnect;
FStream.Free;
raise
end;
You are disconnecting the socket and freeing the stream if an error occurs,
but then you are doing it again in another finally block, which will get
called regardless of an error occuring. So, you are basically disconnecting
the socket twice and freeing the stream twice. You don't need to do that.
Remove the Disconnect() and Free() from the 'except' block altogether, let
the 'finally' block do the work of both. Or better yet, use separate
try...finally block, one for the connection and one for the stream:

procedure TForm1.Button2Click(Sender: TObject);
...
begin
...
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
IdTCPClient1.Connect;
try
finally
IdTCPClient1.Disconnect;
end;
finally
FStream.Free;
end;
end;
Post by Nicke
Application.ProcessMessages;
You do not need to call that. As I explained earlier, the OnExecute event
is triggered in the context of an Indy worker thread, *not* in the context
of the main VCL thread. There is no need to process messages in the worker
thread, the main thread is still running on its own and still process its
own messages normally. Indy does not use messages at all.
Post by Nicke
ReadStreamBuffer := 5000000; //set a read buffer
This is going to be a problem, keep reading...
Post by Nicke
FStream := TFileStream.Create('C:\test.fil', fmCreate);
StreamSize := FStream.Size;
You just created the file anew, so its size will always be 0.
Post by Nicke
memo1.SelText := IntToStr(StreamSize);
Again, do not access the GUI or anything else from the VCL objects of the
main VCL thread unless you use the thread's Synchronize() method. That is
the only safe way to access the main thread's VCL objects from within a
worker thread.
Post by Nicke
ReadStream(FStream, ReadStreamBuffer, False);
This statement is wrong. By specifying an actual size value for the
AByteCount parameter, ReadStream() will not read the size value that is
transmitted by SendStream() in the client,and will treat that value as just
more file data, which will then corrupt the first 4 bytes of the file that
is stored. Since you are specifying 5,000,000 for the AByteCount,
ReadStream() will pre-allocate the file stream to exactly 5,000,000 bytes,
and then expect the client to transmit exactly 5,000,000 bytes and then stop
the reading. Which is not the actual case. If the client sends a file
smaller then 5,000,000 bytes, ReadStream() will sit there waiting for data
that will never come until the socket eventually times out and throws
errors. If the client sends a file that is greater than 5,000,000 bytes,
only the first 5,000,000 bytes will be read and then the reading will stop,
leaving the file incomplete. So get rid of the ReadStreamBuffer variable
altogether, go back to specifying -1 instead:

ReadStream(FStream, -1, False);

That will allow ReadStream() to use the stream size that the client actually
transmits, and correctly read everything that SendStream() actually
transmits.
Post by Nicke
AThread.Connection.Disconnect;
In the code you have shown, you do not need to disconnect the socket. The
client will disconnect itself, and the server will notice that and clean up
the worker thread appropriately.
Post by Nicke
IdTCPClient1.Disconnect;
Again, as I explained earlier, take that out of the server's code
altogether.

With all of that said, your code should look more like the following
instead:


procedure TForm1.Button2Click(Sender: TObject);
const
WriteStreamBuffer := 4096; //flush every 4Kb, for example
var
FStream : TFileStream;
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);

FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
ProgressBar1.Min := 0;
ProgressBar1.Max := FStream.Size;
ProgressBar1.Position := 0;

IdTCPClient1.Connect;
try
Label5.Caption := 'Connected';

IdTCPClient1.OpenWriteBuffer(WriteStreamBuffer);
try
Memo1.Lines.Add('begin to write the stream');
IdTCPClient1.WriteStream(FStream, True, True, 0);

ProgressBar1.Position := FStream.Position;
ProgressBar1.Update;

Memo1.Lines.Add('wrote the stream');
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
finally
FStream.Free;
end;
end;


procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;


Gambit
Nicke
2003-11-20 22:11:24 UTC
Permalink
Thanks alot for the input! Though it does not work with your suggested
method.

Is it ok if I email you a small Zip file with my little proggie (src)?

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Have you came up with anything regarding this yet?
Sorry, I did not get a chance to look at it last night afterall.
Post by Nicke
WriteStreamBuffer := 5000000; //set a write buffer
Unless your file is greater than 4.7 Mb, that threshold will not be hit.
From your code, it seems that you think the thresholds are a fixed buffer
size. They are not. Buffering is a completely different thing. During the
sending, if you are using write buffering, then the data will be stored in
an internal buffer, and then every 5000000 bytes, the buffer will be flushed
to the socket. If you're going to use write buffer (which again, is
generally not needed to begin with), then I would suggest you use a smaller
threshold value.
IdTCPClient1.Connect;
The fact that you are not specifying any timeout parameter to Connect
suggests to me that you are using an older version of Indy, since Connect in
Indy 9 requires a timeout parameter. If you are, in fact, still using an
older version (probably v8) then I suggest you upgrade to a newer version
before you continue.
Post by Nicke
ProgressBar1.Max := StreamSize;
Do you have OnWork... event handlers assigned to the TIdTCPClient? If not,
then setting up a progress bar will be useless, since it won't be updated
during the actual transfer, which kind of defeats the purpose of using a
progress bar in the first place.
Post by Nicke
IdTCPClient1.WriteStream(FStream, True, True, 0);
Ok, you are telling the client socket to send a stream as well as its byte
length. I will address that issue further down in the server code. Keep
reading...
Post by Nicke
except
IdTCPClient1.CancelWriteBuffer;
IdTCPClient1.Disconnect;
FStream.Free;
raise
end;
You are disconnecting the socket and freeing the stream if an error occurs,
but then you are doing it again in another finally block, which will get
called regardless of an error occuring. So, you are basically
disconnecting
Post by Remy Lebeau (TeamB)
the socket twice and freeing the stream twice. You don't need to do that.
Remove the Disconnect() and Free() from the 'except' block altogether, let
the 'finally' block do the work of both. Or better yet, use separate
procedure TForm1.Button2Click(Sender: TObject);
...
begin
...
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
IdTCPClient1.Connect;
try
finally
IdTCPClient1.Disconnect;
end;
finally
FStream.Free;
end;
end;
Post by Nicke
Application.ProcessMessages;
You do not need to call that. As I explained earlier, the OnExecute event
is triggered in the context of an Indy worker thread, *not* in the context
of the main VCL thread. There is no need to process messages in the worker
thread, the main thread is still running on its own and still process its
own messages normally. Indy does not use messages at all.
Post by Nicke
ReadStreamBuffer := 5000000; //set a read buffer
This is going to be a problem, keep reading...
Post by Nicke
FStream := TFileStream.Create('C:\test.fil', fmCreate);
StreamSize := FStream.Size;
You just created the file anew, so its size will always be 0.
Post by Nicke
memo1.SelText := IntToStr(StreamSize);
Again, do not access the GUI or anything else from the VCL objects of the
main VCL thread unless you use the thread's Synchronize() method. That is
the only safe way to access the main thread's VCL objects from within a
worker thread.
Post by Nicke
ReadStream(FStream, ReadStreamBuffer, False);
This statement is wrong. By specifying an actual size value for the
AByteCount parameter, ReadStream() will not read the size value that is
transmitted by SendStream() in the client,and will treat that value as just
more file data, which will then corrupt the first 4 bytes of the file that
is stored. Since you are specifying 5,000,000 for the AByteCount,
ReadStream() will pre-allocate the file stream to exactly 5,000,000 bytes,
and then expect the client to transmit exactly 5,000,000 bytes and then stop
the reading. Which is not the actual case. If the client sends a file
smaller then 5,000,000 bytes, ReadStream() will sit there waiting for data
that will never come until the socket eventually times out and throws
errors. If the client sends a file that is greater than 5,000,000 bytes,
only the first 5,000,000 bytes will be read and then the reading will stop,
leaving the file incomplete. So get rid of the ReadStreamBuffer variable
ReadStream(FStream, -1, False);
That will allow ReadStream() to use the stream size that the client actually
transmits, and correctly read everything that SendStream() actually
transmits.
Post by Nicke
AThread.Connection.Disconnect;
In the code you have shown, you do not need to disconnect the socket. The
client will disconnect itself, and the server will notice that and clean up
the worker thread appropriately.
Post by Nicke
IdTCPClient1.Disconnect;
Again, as I explained earlier, take that out of the server's code
altogether.
With all of that said, your code should look more like the following
procedure TForm1.Button2Click(Sender: TObject);
const
WriteStreamBuffer := 4096; //flush every 4Kb, for example
var
FStream : TFileStream;
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
ProgressBar1.Min := 0;
ProgressBar1.Max := FStream.Size;
ProgressBar1.Position := 0;
IdTCPClient1.Connect;
try
Label5.Caption := 'Connected';
IdTCPClient1.OpenWriteBuffer(WriteStreamBuffer);
try
Memo1.Lines.Add('begin to write the stream');
IdTCPClient1.WriteStream(FStream, True, True, 0);
ProgressBar1.Position := FStream.Position;
ProgressBar1.Update;
Memo1.Lines.Add('wrote the stream');
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
finally
FStream.Free;
end;
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
Gambit
Nicke
2003-11-22 15:32:30 UTC
Permalink
Could you please take a look at my code? Maybe you can see what's wrong with
it. Send me your email address and I will post it to you.

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Have you came up with anything regarding this yet?
Sorry, I did not get a chance to look at it last night afterall.
Post by Nicke
WriteStreamBuffer := 5000000; //set a write buffer
Unless your file is greater than 4.7 Mb, that threshold will not be hit.
From your code, it seems that you think the thresholds are a fixed buffer
size. They are not. Buffering is a completely different thing. During the
sending, if you are using write buffering, then the data will be stored in
an internal buffer, and then every 5000000 bytes, the buffer will be flushed
to the socket. If you're going to use write buffer (which again, is
generally not needed to begin with), then I would suggest you use a smaller
threshold value.
IdTCPClient1.Connect;
The fact that you are not specifying any timeout parameter to Connect
suggests to me that you are using an older version of Indy, since Connect in
Indy 9 requires a timeout parameter. If you are, in fact, still using an
older version (probably v8) then I suggest you upgrade to a newer version
before you continue.
Post by Nicke
ProgressBar1.Max := StreamSize;
Do you have OnWork... event handlers assigned to the TIdTCPClient? If not,
then setting up a progress bar will be useless, since it won't be updated
during the actual transfer, which kind of defeats the purpose of using a
progress bar in the first place.
Post by Nicke
IdTCPClient1.WriteStream(FStream, True, True, 0);
Ok, you are telling the client socket to send a stream as well as its byte
length. I will address that issue further down in the server code. Keep
reading...
Post by Nicke
except
IdTCPClient1.CancelWriteBuffer;
IdTCPClient1.Disconnect;
FStream.Free;
raise
end;
You are disconnecting the socket and freeing the stream if an error occurs,
but then you are doing it again in another finally block, which will get
called regardless of an error occuring. So, you are basically
disconnecting
Post by Remy Lebeau (TeamB)
the socket twice and freeing the stream twice. You don't need to do that.
Remove the Disconnect() and Free() from the 'except' block altogether, let
the 'finally' block do the work of both. Or better yet, use separate
procedure TForm1.Button2Click(Sender: TObject);
...
begin
...
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
IdTCPClient1.Connect;
try
finally
IdTCPClient1.Disconnect;
end;
finally
FStream.Free;
end;
end;
Post by Nicke
Application.ProcessMessages;
You do not need to call that. As I explained earlier, the OnExecute event
is triggered in the context of an Indy worker thread, *not* in the context
of the main VCL thread. There is no need to process messages in the worker
thread, the main thread is still running on its own and still process its
own messages normally. Indy does not use messages at all.
Post by Nicke
ReadStreamBuffer := 5000000; //set a read buffer
This is going to be a problem, keep reading...
Post by Nicke
FStream := TFileStream.Create('C:\test.fil', fmCreate);
StreamSize := FStream.Size;
You just created the file anew, so its size will always be 0.
Post by Nicke
memo1.SelText := IntToStr(StreamSize);
Again, do not access the GUI or anything else from the VCL objects of the
main VCL thread unless you use the thread's Synchronize() method. That is
the only safe way to access the main thread's VCL objects from within a
worker thread.
Post by Nicke
ReadStream(FStream, ReadStreamBuffer, False);
This statement is wrong. By specifying an actual size value for the
AByteCount parameter, ReadStream() will not read the size value that is
transmitted by SendStream() in the client,and will treat that value as just
more file data, which will then corrupt the first 4 bytes of the file that
is stored. Since you are specifying 5,000,000 for the AByteCount,
ReadStream() will pre-allocate the file stream to exactly 5,000,000 bytes,
and then expect the client to transmit exactly 5,000,000 bytes and then stop
the reading. Which is not the actual case. If the client sends a file
smaller then 5,000,000 bytes, ReadStream() will sit there waiting for data
that will never come until the socket eventually times out and throws
errors. If the client sends a file that is greater than 5,000,000 bytes,
only the first 5,000,000 bytes will be read and then the reading will stop,
leaving the file incomplete. So get rid of the ReadStreamBuffer variable
ReadStream(FStream, -1, False);
That will allow ReadStream() to use the stream size that the client actually
transmits, and correctly read everything that SendStream() actually
transmits.
Post by Nicke
AThread.Connection.Disconnect;
In the code you have shown, you do not need to disconnect the socket. The
client will disconnect itself, and the server will notice that and clean up
the worker thread appropriately.
Post by Nicke
IdTCPClient1.Disconnect;
Again, as I explained earlier, take that out of the server's code
altogether.
With all of that said, your code should look more like the following
procedure TForm1.Button2Click(Sender: TObject);
const
WriteStreamBuffer := 4096; //flush every 4Kb, for example
var
FStream : TFileStream;
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
ProgressBar1.Min := 0;
ProgressBar1.Max := FStream.Size;
ProgressBar1.Position := 0;
IdTCPClient1.Connect;
try
Label5.Caption := 'Connected';
IdTCPClient1.OpenWriteBuffer(WriteStreamBuffer);
try
Memo1.Lines.Add('begin to write the stream');
IdTCPClient1.WriteStream(FStream, True, True, 0);
ProgressBar1.Position := FStream.Position;
ProgressBar1.Update;
Memo1.Lines.Add('wrote the stream');
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
finally
FStream.Free;
end;
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
Gambit
Remy Lebeau (TeamB)
2003-11-22 22:47:54 UTC
Permalink
Post by Nicke
Send me your email address and I will post it to you.
My email address is on every message I post. However, please do not ask for
help via private email. It is against Borland's newsgroup guidelines
(http://www.borland.com/newsgroups):

Content

"9. Do not ask for assistance via email. Saying, "I don't read this
group often, so please mail your replies" is essentially saying, "My time is
more important than your time." If someone is willing to spend the time to
answer your question, you should be willing to spend the time necessary to
retrieve the answer. Getting one-on-one help via e-mail is also known as
consulting, and consulting does not come free."


Gambit
Nicke
2003-11-22 23:17:33 UTC
Permalink
Well, I agree on that. But since you are not suposed to attach any files to
your posts I thought it would be easier if I sent you an mail with my
source. I'll post the code here instead. The problem is I get 0 byte sized
files. Also i have started to fix the progressbar. Please give me some
comments on that one.

How do I know where in the transfer I am? I would like to know how many
percent of the transfer is done etc. Do you have any ideas how to get the
data to calculate the current transfer speed in Kb/s (KB/s)?

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Code
procedure TForm1.Button2Click(Sender: TObject);
var
FStream : TFileStream;

begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
IdTCPClient1.Connect;
Label5.Caption := 'Connected';

FStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);

try
IdTCPClient1.WriteStream(FStream);
finally
Label5.Caption := 'Disconnected';
IdTCPClient1.Disconnect;

FStream.Free;
end;
end;


procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream1 : TFileStream;
begin
FStream1 := TFileStream.Create('C:\test.fil', fmCreate);
try
IdTCPClient1.ReadStream(FStream1, -1 ,False);
finally
FStream1.Free;
end;
end;


procedure TForm1.IdTCPClient1WorkBegin(Sender: TObject;
AWorkMode: TWorkMode; const AWorkCountMax: Integer);
begin
ProgressBar1.Min := 0;
ProgressBar1.Max := AWorkCountMax;
end;


procedure TForm1.IdTCPClient1Work(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
ProgressBar1.Position := AWorkCount;
end;


procedure TForm1.IdTCPClient1WorkEnd(Sender: TObject;
AWorkMode: TWorkMode);
begin
ProgressBar1.Position := ProgressBar1.Max
end;


<< Code
Post by Remy Lebeau (TeamB)
Post by Nicke
Send me your email address and I will post it to you.
My email address is on every message I post. However, please do not ask for
help via private email. It is against Borland's newsgroup guidelines
Content
"9. Do not ask for assistance via email. Saying, "I don't read this
group often, so please mail your replies" is essentially saying, "My time is
more important than your time." If someone is willing to spend the time to
answer your question, you should be willing to spend the time necessary to
retrieve the answer. Getting one-on-one help via e-mail is also known as
consulting, and consulting does not come free."
Gambit
Remy Lebeau (TeamB)
2003-11-22 23:30:43 UTC
Permalink
Post by Nicke
Well, I agree on that. But since you are not suposed to attach
any files to your posts I thought it would be easier if I sent you
an mail with my source.
Attachments can be posted to the .attachments group.
Post by Nicke
ProgressBar1.Position := AWorkCount;
In the OnWork event, AWorkCount is not a runing total of all the data that
has been transferred. It is the size of just the current buffer that is
transmitted. Sending large amounts of data will be sent using multiple
buffer packets, with the OnWork event being triggered for each one. You
need to add the AWorkCount value to the current progressbar value, not
assign it as-is:

ProgressBar1.Position := ProgressBar1.Position + AWorkCount;


Gambit
Remy Lebeau (TeamB)
2003-11-22 23:26:45 UTC
Permalink
Post by Nicke
Could you please take a look at my code?
I already did 2 days ago, and provided my suggestions. You said they did
not work, but you did not explain further as to why.

Looking into it, I can see that the OnExecute event is being triggered
multiple times. The first time, it stores the whole file as expected, but
then the second time, because you are using fmCreate for your TFileStream,
the file that was stored the first time is cleared out and overwritten, but
since there is no more data transmitted, there is no more data to store.
That is why the file is empty.

You have a couple of different options to handle this situation:

1) have the server close the client connection after the file is stored:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
AThread.Connection.Disconnect;
finally
FStream.Free;
end;
end;

2) have the server call ReadFromStack() to make sure data is actually
available before opening the file:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream: TFileStream;
begin
if AThread.Connection.ReadFromStack(False, 5000, False) > 0 then
begin
FStream = TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
end;


Gambit
Nicke
2003-11-23 03:29:50 UTC
Permalink
Thanks! I will see if I can get it right. I'll keep you posted.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Could you please take a look at my code?
I already did 2 days ago, and provided my suggestions. You said they did
not work, but you did not explain further as to why.
Looking into it, I can see that the OnExecute event is being triggered
multiple times. The first time, it stores the whole file as expected, but
then the second time, because you are using fmCreate for your TFileStream,
the file that was stored the first time is cleared out and overwritten, but
since there is no more data transmitted, there is no more data to store.
That is why the file is empty.
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
AThread.Connection.Disconnect;
finally
FStream.Free;
end;
end;
2) have the server call ReadFromStack() to make sure data is actually
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream: TFileStream;
begin
if AThread.Connection.ReadFromStack(False, 5000, False) > 0 then
begin
FStream = TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
end;
Gambit
Nicke
2003-11-23 18:23:17 UTC
Permalink
I have tried to send you emails, but your email seems not to be valid.
Anyway I can't the file transfer to work as it should. (OnExecute is only
called once when I try it). I will post the full source of the program in
the attachement forum. Please take a look at it. (aserv.zip).

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Could you please take a look at my code?
I already did 2 days ago, and provided my suggestions. You said they did
not work, but you did not explain further as to why.
Looking into it, I can see that the OnExecute event is being triggered
multiple times. The first time, it stores the whole file as expected, but
then the second time, because you are using fmCreate for your TFileStream,
the file that was stored the first time is cleared out and overwritten, but
since there is no more data transmitted, there is no more data to store.
That is why the file is empty.
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
AThread.Connection.Disconnect;
finally
FStream.Free;
end;
end;
2) have the server call ReadFromStack() to make sure data is actually
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream: TFileStream;
begin
if AThread.Connection.ReadFromStack(False, 5000, False) > 0 then
begin
FStream = TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
end;
Gambit
Remy Lebeau (TeamB)
2003-11-23 23:54:04 UTC
Permalink
Post by Nicke
I have tried to send you emails, but your email seems not to be valid.
My guess would be that you did not remove the "no.spam" portions first.
Post by Nicke
Anyway I can't the file transfer to work as it should.
The code I posted in my last message forked fine for me. I tested it before
I posted it.


Gambit
Nicke
2003-11-24 09:31:54 UTC
Permalink
Real strange. It is not working for me. Did you take a look on the code I
posted to the attachement forum? I'm using the newest Indy components
9.0.14.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
I have tried to send you emails, but your email seems not to be valid.
My guess would be that you did not remove the "no.spam" portions first.
Post by Nicke
Anyway I can't the file transfer to work as it should.
The code I posted in my last message forked fine for me. I tested it before
I posted it.
Gambit
Nicke
2003-11-27 18:51:15 UTC
Permalink
Gambit, did you take a look at the code I posted?

/Nicke
Post by Nicke
Real strange. It is not working for me. Did you take a look on the code I
posted to the attachement forum? I'm using the newest Indy components
9.0.14.
/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
I have tried to send you emails, but your email seems not to be valid.
My guess would be that you did not remove the "no.spam" portions first.
Post by Nicke
Anyway I can't the file transfer to work as it should.
The code I posted in my last message forked fine for me. I tested it
before
Post by Remy Lebeau (TeamB)
I posted it.
Gambit
Remy Lebeau (TeamB)
2003-11-27 23:01:29 UTC
Permalink
Post by Nicke
Gambit, did you take a look at the code I posted?
No, I have not had any chance to look at it yet. I probably won't until
this weekend, after the holidays are over (well, this week's holiday's
anyway).


Gambit
Nicke
2003-11-28 11:04:29 UTC
Permalink
Great I have reinstalled Indy now (9.0.14). Connect now requires a timeout
value. But it will still not work.

I will post my most recent version of the code in the attachement forum.
Subject will be "To Gambit".

Thanks for helping out.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Gambit, did you take a look at the code I posted?
No, I have not had any chance to look at it yet. I probably won't until
this weekend, after the holidays are over (well, this week's holiday's
anyway).
Gambit
Nicke
2003-11-30 20:07:57 UTC
Permalink
Hi!

I have posted the new code today in the attachements group.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Gambit, did you take a look at the code I posted?
No, I have not had any chance to look at it yet. I probably won't until
this weekend, after the holidays are over (well, this week's holiday's
anyway).
Gambit
Nicke
2003-11-27 18:52:02 UTC
Permalink
Gambit, did you take a look at the code I posted?

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
I have tried to send you emails, but your email seems not to be valid.
My guess would be that you did not remove the "no.spam" portions first.
Post by Nicke
Anyway I can't the file transfer to work as it should.
The code I posted in my last message forked fine for me. I tested it before
I posted it.
Gambit
Nicke
2003-11-22 15:32:58 UTC
Permalink
Could you please take a look at my code? Maybe you can see what's wrong with
it. Send me your email address and I will post it to you.

Thanks.

/Nicke
Post by Remy Lebeau (TeamB)
Post by Nicke
Have you came up with anything regarding this yet?
Sorry, I did not get a chance to look at it last night afterall.
Post by Nicke
WriteStreamBuffer := 5000000; //set a write buffer
Unless your file is greater than 4.7 Mb, that threshold will not be hit.
From your code, it seems that you think the thresholds are a fixed buffer
size. They are not. Buffering is a completely different thing. During the
sending, if you are using write buffering, then the data will be stored in
an internal buffer, and then every 5000000 bytes, the buffer will be flushed
to the socket. If you're going to use write buffer (which again, is
generally not needed to begin with), then I would suggest you use a smaller
threshold value.
IdTCPClient1.Connect;
The fact that you are not specifying any timeout parameter to Connect
suggests to me that you are using an older version of Indy, since Connect in
Indy 9 requires a timeout parameter. If you are, in fact, still using an
older version (probably v8) then I suggest you upgrade to a newer version
before you continue.
Post by Nicke
ProgressBar1.Max := StreamSize;
Do you have OnWork... event handlers assigned to the TIdTCPClient? If not,
then setting up a progress bar will be useless, since it won't be updated
during the actual transfer, which kind of defeats the purpose of using a
progress bar in the first place.
Post by Nicke
IdTCPClient1.WriteStream(FStream, True, True, 0);
Ok, you are telling the client socket to send a stream as well as its byte
length. I will address that issue further down in the server code. Keep
reading...
Post by Nicke
except
IdTCPClient1.CancelWriteBuffer;
IdTCPClient1.Disconnect;
FStream.Free;
raise
end;
You are disconnecting the socket and freeing the stream if an error occurs,
but then you are doing it again in another finally block, which will get
called regardless of an error occuring. So, you are basically
disconnecting
Post by Remy Lebeau (TeamB)
the socket twice and freeing the stream twice. You don't need to do that.
Remove the Disconnect() and Free() from the 'except' block altogether, let
the 'finally' block do the work of both. Or better yet, use separate
procedure TForm1.Button2Click(Sender: TObject);
...
begin
...
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
IdTCPClient1.Connect;
try
finally
IdTCPClient1.Disconnect;
end;
finally
FStream.Free;
end;
end;
Post by Nicke
Application.ProcessMessages;
You do not need to call that. As I explained earlier, the OnExecute event
is triggered in the context of an Indy worker thread, *not* in the context
of the main VCL thread. There is no need to process messages in the worker
thread, the main thread is still running on its own and still process its
own messages normally. Indy does not use messages at all.
Post by Nicke
ReadStreamBuffer := 5000000; //set a read buffer
This is going to be a problem, keep reading...
Post by Nicke
FStream := TFileStream.Create('C:\test.fil', fmCreate);
StreamSize := FStream.Size;
You just created the file anew, so its size will always be 0.
Post by Nicke
memo1.SelText := IntToStr(StreamSize);
Again, do not access the GUI or anything else from the VCL objects of the
main VCL thread unless you use the thread's Synchronize() method. That is
the only safe way to access the main thread's VCL objects from within a
worker thread.
Post by Nicke
ReadStream(FStream, ReadStreamBuffer, False);
This statement is wrong. By specifying an actual size value for the
AByteCount parameter, ReadStream() will not read the size value that is
transmitted by SendStream() in the client,and will treat that value as just
more file data, which will then corrupt the first 4 bytes of the file that
is stored. Since you are specifying 5,000,000 for the AByteCount,
ReadStream() will pre-allocate the file stream to exactly 5,000,000 bytes,
and then expect the client to transmit exactly 5,000,000 bytes and then stop
the reading. Which is not the actual case. If the client sends a file
smaller then 5,000,000 bytes, ReadStream() will sit there waiting for data
that will never come until the socket eventually times out and throws
errors. If the client sends a file that is greater than 5,000,000 bytes,
only the first 5,000,000 bytes will be read and then the reading will stop,
leaving the file incomplete. So get rid of the ReadStreamBuffer variable
ReadStream(FStream, -1, False);
That will allow ReadStream() to use the stream size that the client actually
transmits, and correctly read everything that SendStream() actually
transmits.
Post by Nicke
AThread.Connection.Disconnect;
In the code you have shown, you do not need to disconnect the socket. The
client will disconnect itself, and the server will notice that and clean up
the worker thread appropriately.
Post by Nicke
IdTCPClient1.Disconnect;
Again, as I explained earlier, take that out of the server's code
altogether.
With all of that said, your code should look more like the following
procedure TForm1.Button2Click(Sender: TObject);
const
WriteStreamBuffer := 4096; //flush every 4Kb, for example
var
FStream : TFileStream;
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
FStream := TFileStream.Create(FileName, fmOpenRead or
fmShareDenyWrite);
try
ProgressBar1.Min := 0;
ProgressBar1.Max := FStream.Size;
ProgressBar1.Position := 0;
IdTCPClient1.Connect;
try
Label5.Caption := 'Connected';
IdTCPClient1.OpenWriteBuffer(WriteStreamBuffer);
try
Memo1.Lines.Add('begin to write the stream');
IdTCPClient1.WriteStream(FStream, True, True, 0);
ProgressBar1.Position := FStream.Position;
ProgressBar1.Update;
Memo1.Lines.Add('wrote the stream');
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
finally
FStream.Free;
end;
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
begin
FStream := TFileStream.Create('C:\test.fil', fmCreate);
try
AThread.Connection.ReadStream(FStream, -1, False);
finally
FStream.Free;
end;
end;
Gambit
Nicke
2003-11-19 13:34:57 UTC
Permalink
This is how the code looks today. Things seems to work besides that my files
end up beeing 32K and "empty". Also the progressbar doesnt get updated while
the buffers are read/written. It also seems like the server doesnt know the
FStream.Size cos I can't get it to show anything in the progressbar.

Please advise.

Thanks.

/Nicke

procedure TForm1.Button2Click(Sender: TObject);
var
AThread: TIdPeerThread;
FStream : TFileStream;
WriteStreamBuffer : Integer;
StreamSize : Int64;

begin
WriteStreamBuffer := 5000000; //set a write buffer

IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);

if IdTCPClient1.Connected then
IdTCPClient1.Disconnect;

IdTCPClient1.Connect;
Label5.Caption := 'Connected';

FStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
StreamSize := FStream.Size;

ProgressBar1.Min := 0;
ProgressBar1.Max := StreamSize;
ProgressBar1.Position := 0;

with IdTCPClient1 do
try
IdTCPClient1.OpenWriteBuffer(WriteStreamBuffer);
try
memo1.SelText := 'begin to write the stream' + #13#10;
IdTCPClient1.WriteStream(FStream, True, True, 0);

ProgressBar1.Position := FStream.Position;
ProgressBar1.Update;

memo1.SelText := 'wrote the stream' + #13#10;
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
IdTCPClient1.Disconnect;
FStream.Free;
raise
end;
finally
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
FStream.Free;
end;
end;


procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
FStream : TFileStream;
ReadStreamBuffer : Integer;
StreamSize : Int64;
sCmd : String;

begin
Application.ProcessMessages;

ReadStreamBuffer := 5000000; //set a read buffer

//FStream := TFileStream.Create('C:\' + ExtractFileName(FileName),
fmCreate);
FStream := TFileStream.Create('C:\test.fil', fmCreate);
StreamSize := FStream.Size;
memo1.SelText := IntToStr(StreamSize);

//ProgressBar1.Min := 0;
//ProgressBar1.Max := StreamSize;
//ProgressBar1.Position := 0;

with AThread.Connection do
try
memo1.SelText := 'start write' + #13#10;
ReadStream(FStream, ReadStreamBuffer, False);
//ProgressBar1.Position := FStream.Position;
//ProgressBar1.Update;
memo1.SelText := 'start write second' + #13#10;
finally
memo1.SelText := 'end write' + #13#10;
AThread.Connection.Disconnect;
IdTCPClient1.Disconnect;
FStream.Free;
memo1.SelText := 'disconnect write' + #13#10;
end;
end;
Post by Nicke
Hi!
I'm fairly new to Delphi programming.. anyway my problem is that when i send
a file through the client -> server utlilising the Indy component my file
end up beeing ZERO size. I will include my unit1.pas file here so
you can take a look on what's wrong.
Thanks.
Nicke
Post by Nicke
unit1.pas
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs,
Menus, CoolTrayIcon, IdBaseComponent, IdComponent, IdTCPServer, StdCtrls,
Sockets, TypInfo, ComCtrls, Registry, XPMan, ExtCtrls, IdTCPConnection,
IdTCPClient;
type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
TrayIcon1: TCoolTrayIcon;
PopupMenu1: TPopupMenu;
Showwindow1: TMenuItem;
Exit1: TMenuItem;
About1: TMenuItem;
MainMenu1: TMainMenu;
File1: TMenuItem;
About2: TMenuItem;
Exit2: TMenuItem;
XPManifest1: TXPManifest;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
GroupBox1: TGroupBox;
Memo1: TMemo;
GroupBox2: TGroupBox;
CheckBox1: TCheckBox;
Label1: TLabel;
CheckBox2: TCheckBox;
LabeledEdit1: TLabeledEdit;
Label2: TLabel;
TabSheet3: TTabSheet;
OpenDialog1: TOpenDialog;
StatusBar1: TStatusBar;
GroupBox3: TGroupBox;
Button1: TButton;
Edit1: TEdit;
Label3: TLabel;
Label4: TLabel;
Button2: TButton;
Label6: TLabel;
GroupBox4: TGroupBox;
LabeledEdit2: TLabeledEdit;
Button3: TButton;
ProgressBar1: TProgressBar;
Label7: TLabel;
IdTCPClient1: TIdTCPClient;
Button4: TButton;
Label5: TLabel;
LabeledEdit3: TLabeledEdit;
SaveDialog1: TSaveDialog;
procedure TrayIcon1Startup(Sender: TObject; var ShowMainForm: Boolean);
procedure TrayIcon1MinimizeToTray(Sender: TObject);
procedure ShowWindow1Click(Sender: TObject);
procedure IdTCPServer1Execute(AThread: TIdPeerThread);
procedure IdTCPServer1Connect(AThread: TIdPeerThread);
procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
procedure Exit1Click(Sender: TObject);
procedure About1Click(Sender: TObject);
procedure About2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
Variant;
procedure SaveSetting(Key, Item, RegType: String; Value: Variant);
procedure CheckBox1Click(Sender: TObject);
procedure LoadRegSettings();
procedure SetNetworkConfig();
procedure FormCreate(Sender: TObject);
procedure CheckBox2Click(Sender: TObject);
procedure LabeledEdit1Change(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure PageControl1Change(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure LabeledEdit2Change(Sender: TObject);
procedure LabeledEdit3Change(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type serverCommands = (QUIT, SEND, SHUTDOWN, PING);
const
ASERV_MAJOR_VERSION = 'BETA 01';
serverMsg = 'Message from server: ';
REGKEY_SETTINGS = 'Software\aServ\Settings';
var
SysTrayConnectionNotify : Boolean;
StartMinimized : Boolean;
ListenOnPort : Integer;
RemoteHost : String;
RemoteHostPort : Integer;
S : TMemoryStream;
FileName : String;
FStream : TFileStream;
procedure TForm1.SetNetworkConfig();
begin
//set the default port and interface to listen on
IdTCPServer1.Bindings.Add.IP := '0.0.0.0';
IdTCPServer1.Bindings.Add.Port := ListenOnPort;
//activate the server
IdTCPServer1.Active := True;
end;
procedure TForm1.LoadRegSettings();
begin
Application.ProcessMessages;
//what remote host should we connect to
RemoteHost := LoadSetting(REGKEY_SETTINGS, 'RemoteHost', 'String',
'localhost');
LabeledEdit2.Text := RemoteHost;
//what remote port should we connect to
RemoteHostPort := LoadSetting(REGKEY_SETTINGS, 'RemoteHostPort', 'String',
'8000');
LabeledEdit3.Text := IntToStr(RemoteHostPort);
//should we be notified about connection events when minimized to systray
SysTrayConnectionNotify := LoadSetting(REGKEY_SETTINGS,
'SysTrayConnectionNotify', 'Boolean', False);
CheckBox1.Checked := SysTrayConnectionNotify;
//should we start minimized to systray
ListenOnPort := LoadSetting(REGKEY_SETTINGS, 'ListenOnPort', 'String',
'8000');
LabeledEdit1.Text := IntToStr(ListenOnPort);
//what port should we listen on
StartMinimized := LoadSetting(REGKEY_SETTINGS, 'StartMinimized',
'Boolean', False);
CheckBox2.Checked := StartMinimized;
Label1.Caption := 'Settings reloaded!';
Label1.Update;
Sleep(50);
Label1.Caption := ' ';
Label1.Update;
end;
Variant;
var
Reg: TRegIniFile;
begin
Reg := TRegIniFile.Create(Key);
if RegType = 'String' then
Result := Reg.ReadString('', Item, DefValue)
else if RegType = 'Boolean' then
Result := Reg.ReadBool('', Item, DefValue);
Reg.Free;
end;
procedure TForm1.SaveSetting(Key, Item, RegType: String; Value: Variant);
var
Reg: TRegIniFile; > begin
Reg := TRegIniFile.Create(Key);
if RegType = 'String' then
Reg.WriteString('', Item, Value)
else if RegType = 'Boolean' then
Reg.WriteBool('', Item, Value);
Reg.Free;
end;
Boolean);
begin
//load our settings from the registry
LoadRegSettings();
if StartMinimized then
begin
//show our main form
ShowMainForm := False;
//hide the systray icon
TrayIcon1.IconVisible := True;
end
else
begin
//hide our main form (start in systray)
ShowMainForm := True;
//show the systray icon
TrayIcon1.IconVisible := False;
//load our settings from the registry
end;
end;
procedure TForm1.ShowWindow1Click(Sender: TObject);
begin
//hide the systray icon when we acticvate the main window
TrayIcon1.IconVisible :=False;
//show our main window
TrayIcon1.ShowMainForm; // ALWAYS use this method!!!
end;
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
sCmd : String;
begin
FStream := TFileStream.Create('C:\' + ExtractFileName(FileName), fmCreate
or fmShareExclusive);
with AThread.Connection do
try
memo1.SelText := 'start write' + #13#10;
ReadStream(FStream, -1, False);
memo1.SelText := 'start write second' + #13#10;
finally
memo1.SelText := 'end write' + #13#10;
IdTCPClient1.Disconnect;
FStream.Free;
memo1.SelText := 'disconnect write' + #13#10;
end;
end;
procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
//display a messsage when a client has connected
memo1.SelText := 'Client connected: ' + FormatDateTime('hh:mm:ss', now) +
' [IP: ' + AThread.Connection.Socket.Binding.PeerIP +']' + #13#10;
if SysTrayConnectionNotify then
TrayIcon1.ShowBalloonHint('Connection event', 'Client connected from: '
+ AThread.Connection.Socket.Binding.PeerIP, bitInfo, 10);
//greeting message
AThread.Connection.WriteLn('Greetings!');
end;
procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
//display a messsage when a client has disconnected
memo1.SelText := 'Client disconnected: ' + FormatDateTime('hh:mm:ss', now)
+ #13#10;
end;
procedure TForm1.Exit1Click(Sender: TObject);
begin
//exit our application
Application.Terminate;
end;
procedure TForm1.About1Click(Sender: TObject);
begin
//TrayIcon1.ShowBalloonHint('aServ' + ASERV_MAJOR_VERSION , 'Please visit
http://www.nicke.nu', bitInfo, 10);
if SysTrayConnectionNotify then
TrayIcon1.ShowBalloonHint('aServ ' + ASERV_MAJOR_VERSION, 'Förra året
fick Aftonbladets läsare dela på en miljon kronor för att sätta guldkant på
tillvaron. Gensvaret var enormt och succén omedelbar.' + 'Över en halv
miljon ansökningar kom in där människor önskade sig allt från nya skolböcker
till prinsesskalas. 130 personer fick slutligen sin önskan uppfylld.',
bitInfo, 10);
end;
procedure TForm1.About2Click(Sender: TObject);
begin
//display an about box
ShowMessage('aServ ' + ASERV_MAJOR_VERSION + #13#10 + 'Copyright 2003
nicke.nu');
end;
procedure TForm1.TrayIcon1MinimizeToTray(Sender: TObject);
begin
//don't show the form in the taskbar
TrayIcon1.HideTaskbarIcon;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//setup networking stuff
SetNetworkConfig();
if not Memo1.SelLength <= 0 then
Memo1.Text := ' ';
end;
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if CheckBox1.Checked then
begin
SaveSetting(REGKEY_SETTINGS, 'SysTrayConnectionNotify', 'Boolean',
True);
end
else
begin
SaveSetting(REGKEY_SETTINGS, 'SysTrayConnectionNotify', 'Boolean',
False);
end;
//load our settings from the registry
LoadRegSettings();
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
if CheckBox2.Checked then
begin
SaveSetting(REGKEY_SETTINGS, 'StartMinimized', 'Boolean', True);
end
else
begin
SaveSetting(REGKEY_SETTINGS, 'StartMinimized', 'Boolean', False);
end;
//load our settings from the registry
LoadRegSettings();
end;
procedure TForm1.LabeledEdit1Change(Sender: TObject);
begin
if LabeledEdit1.Text = '0' then
else if LabeledEdit1.Text = '' then
else
SaveSetting(REGKEY_SETTINGS, 'ListenOnPort', 'String',
LabeledEdit1.Text);
end;
procedure TForm1.Button1Click(Sender: TObject);
var FSizeKB : Double;
var FSizeMB : Double;
begin
if OpenDialog1.Execute then
begin
FileName := OpenDialog1.FileName;
S := TMemoryStream.Create;
try
if FileExists(FileName) then
begin
//display the 'File size' label
Label3.Visible := True;
//set the decimal seperator character
DecimalSeparator := '.';
//load our file
S.LoadFromFile(FileName);
//setup file size formats
FSizeKB := S.Size / 1024;
FSizeMB := FSizeKB / 1024;
//if file size is smaller then one KB display in bytes
if S.Size <= 1024 then
Label4.Caption := IntToStr(S.Size) + ' bytes'
// if file size is bigger then one KB but smaller then one MB
display in KB
// else display in MB
else
if FSizeKB >= 1024 then
Label4.Caption := FormatFloat('#.00', FSizeMB) + ' MB ' +
'(' + FormatFloat('0', FSizeKB) + ' KB)'
else
Label4.Caption := FormatFloat('0', FSizeKB) + ' KB';
Edit1.Text := FileName;
StatusBar1.Panels[0].Text := 'Filename: ' + FileName;
end
else
//if file not found
MessageDlg('File not found! Try again.', mtError , [mbOK], 0);
finally
S.Free;
end;
end;
end;
procedure TForm1.PageControl1Change(Sender: TObject);
begin
StatusBar1.Panels[0].Text := ' ';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
try
if LabeledEdit2.Text = '' then
MessageDlg('You must enter a host to connect to.', mtError , [mbOK],
0)
else if LabeledEdit3.Text = '' then
MessageDlg('You must enter a port.', mtError , [mbOK], 0)
else
begin
if not IdTCPClient1.Connected then
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
IdTCPClient1.Connect;
Label5.Caption := 'Connected';
end;
end;
finally
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
if IdTCPClient1.Connected then
begin
IdTCPClient1.Disconnect;
Label5.Caption := 'Disconnected';
end;
end;
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
//disconnect our client connections if any
if IdTCPClient1.Connected then
IdTCPClient1.Disconnect;
end;
procedure TForm1.LabeledEdit2Change(Sender: TObject);
begin
SaveSetting(REGKEY_SETTINGS, 'RemoteHost', 'String', LabeledEdit2.Text);
end;
procedure TForm1.LabeledEdit3Change(Sender: TObject);
begin
SaveSetting(REGKEY_SETTINGS, 'RemoteHostPort', 'String',
LabeledEdit3.Text);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
IdTCPClient1.Host := LabeledEdit2.Text;
IdTCPClient1.Port := StrToInt(LabeledEdit3.Text);
IdTCPClient1.Connect;
FStream := TFileStream.Create(FileName, fmOpenRead);
with IdTCPClient1 do
try
IdTCPClient1.OpenWriteBuffer;
try
IdTCPClient1.WriteStream(FStream, True, True, 0);
IdTCPClient1.CloseWriteBuffer;
except
IdTCPClient1.CancelWriteBuffer;
raise
end;
finally
//IdTCPClient1.Disconnect;
FStream.Free;
end;
end;
end.
<<<<<< unit1.pas
Loading...