Board index » delphi » IdTelnet (Indy 10): Multi - Telnet sessions.

IdTelnet (Indy 10): Multi - Telnet sessions.


2006-07-28 01:30:17 AM
delphi249
I am trying to create an application that connects(telnet) to
multiple(8 to 20+) unix boxes and executes a list of several commands.
The list of servers is loaded via an INI file. A TMemo and TIdTelnet
are placed on a frame and are created dynamically on a TTabSheet(in a
TPageControl) base on the number of entries in the INI file.
So far it seems to work for about 3 or less connections. On the fourth
and more, I start getting hangs. The application becomes unresponsive.
Occasionally it returns - only to become unresponsive again.
I am alittle unsure as to which way to go now.
I suspect my design is at fault, but it could simply be my
implementation. Am I being unrealistic? I am thinking that this
should be accomplishable for the magnitude(20-25) that I am looking at.
I haven't created threads for each dynamic tab - I assume some
threading is done in the component, but not sure exactly how that
playes into it. Do I need to create my own threads for each?
I'll post code so as to skip that request ;-) Thanks in advance for
any help. any streamlining on the component searching would be nice
also. It is 280 lines or so including Francois Piette's demo code for
the DataAvailable. I use long lines to(well past 80 sometimes) -
sorry.
unit fOctopus;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
Menus, StdCtrls, ComCtrls, ToolWin, IniFiles, Dialogs, ImgList,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdTelnet,
fTelnet;
//******************************************************************************
type
TSite = record
site : string;
visn : string;
moniker : string;
id : string;
server : string;
port : integer;
FContinue: boolean;
end;
//******************************************************************************
TfrmOctopus = class(TForm)
MainMenu1: TMainMenu;
statusBar: TStatusBar;
File1: TMenuItem;
Connection1: TMenuItem;
Help1: TMenuItem;
ToolBar1: TToolBar;
tbtnConnect: TToolButton;
tbtnLogin: TToolButton;
tbtnUpCarat: TToolButton;
ToolButton4: TToolButton;
ToolButton5: TToolButton;
ToolButton6: TToolButton;
ToolButton7: TToolButton;
ToolButton8: TToolButton;
ToolButton9: TToolButton;
ToolButton10: TToolButton;
tbtnDisconnect: TToolButton;
memoSites: TMemo;
ToolButton12: TToolButton;
imagesEnabled: TImageList;
imagesDisabled: TImageList;
tabPage: TPageControl;
editAccess: TEdit;
ToolButton1: TToolButton;
editVerify: TEdit;
lblVerify: TLabel;
ToolButton13: TToolButton;
lblAccess: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Connect(Sender: TObject);
procedure Disconnect(Sender: TObject);
//------------------------------------------------
procedure telnetClientDataAvailable(Sender: TIdTelnet; const
Buffer: String);
procedure telnetClientStatus(ASender: TObject; const AStatus:
TIdStatus; const AStatusText: String);
//------------------------------------------------
private
{ Private declarations }
FOctopusINI: TIniFile;
FSites: array of TSite;
FslSitelist: TStringlist;
public
{ Public declarations }
procedure ImportINI;
procedure SetSites(i: integer);
procedure SendCommand(S: string; telnetToSendTo: TIdTelnet);
end;
//******************************************************************************
var
frmOctopus: TfrmOctopus;
implementation
{$R *.dfm}
//******************************************************************************
procedure TfrmOctopus.ImportINI;
var
iniSectionName: string;
i: integer;
begin
memoSites.Clear;
FOctopusINI := TIniFile.Create(ExtractFilePath(ParamStr(0)) +
'Octopus.ini');
with FOctopusINI do
begin
FslSitelist := TStringlist.Create;
FslSitelist.Clear;
FOctopusINI.ReadSection('Sites', FslSitelist);
if (FslSitelist.Count>0) then
begin
SetLength(FSites, FslSitelist.Count);
for i := 0 to (FslSitelist.Count - 1) do
begin
iniSectionName := FOctopusINI.ReadString('Sites', 'site' +
IntToStr(Ord(i)), '');
with FSites[i] do
begin
site := FOctopusINI.ReadString(iniSectionName, 'site',
'');
visn := FOctopusINI.ReadString(iniSectionName, 'visn',
'');
moniker := FOctopusINI.ReadString(iniSectionName,
'moniker', '');
id := FOctopusINI.ReadString(iniSectionName, 'id',
'');
server := FOctopusINI.ReadString(iniSectionName,
'server', '');
port := FOctopusINI.ReadInteger(iniSectionName, 'port',
0);
FContinue := TRUE;
end;
memoSites.Lines.Add(FSites[i].site);
end;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.SetSites(i: integer);
var
tabClients: TTabSheet;
frameClients: TframeClient;
begin
tabClients := TTabSheet.Create(tabPage);
tabClients.PageControl := tabPage;
tabClients.Caption := FSites[i].moniker;
tabClients.Tag := i;
frameClients := TframeClient.Create(tabClients);
frameClients.Parent := tabClients;
frameClients.Align := alClient;
frameClients.telnetClient.Host := FSites[i].server;
frameClients.telnetClient.OnStatus := telnetClientStatus;
frameClients.telnetClient.OnDataAvailable :=
telnetClientDataAvailable;
end;
//******************************************************************************
procedure TfrmOctopus.FormCreate(Sender: TObject);
var
i: integer;
begin
ImportINI;
for i := 0 to FslSiteList.Count - 1 do SetSites(i);
end;
//******************************************************************************
procedure TfrmOctopus.SendCommand(S: string; telnetToSendTo:
TIdTelnet);
var
i: integer;
begin
for i := 1 to length(S) do telnetToSendTo.SendCh(S[i]);
telnetToSendTo.SendCh(#13);
end;
//******************************************************************************
procedure TfrmOctopus.Connect(Sender: TObject);
var
s: string;
i, j, k, l: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if not((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
begin
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connect;
l := tabPage.Pages[i].Tag;
tabPage.Pages[i].Tag := (tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.TelnetThread.ThreadID;
sleep(500);
s := UPPERCASE(FSites[l].moniker) + 'VISTA';
for k := 1 to length(s) do (tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.SendCh(s[k]);
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.SendCh(#13);
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.Disconnect(Sender: TObject);
var
i, j: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if ((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
begin
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Disconnect;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.FormDestroy(Sender: TObject);
var
i, j: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if not((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
begin
if ((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Disconnect;
end;
end;
FslSitelist.Free;
FOctopusINI.Free;
end;
//******************************************************************************
procedure TfrmOctopus.telnetClientDataAvailable(Sender: TIdTelnet;
const Buffer: String);
const
CR = #13;
LF = #10;
var
Start, Stop, iCurrentPID, i, j: Integer;
begin
iCurrentPID := Sender.TelnetThread.ThreadID;
for i := 0 to tabPage.PageCount - 1 do
begin
if tabPage.Pages[i].Tag = iCurrentPID then // Sync tab
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
begin // Find frame (only
control, but for later...)
//------------------------------------------------
{This routine comes directly from the ICS TNDEMO code. Thanks to
Francois Piette
It updates the memo control when we get data}
if ((tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Count = 0) then
(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Add('');
Start := 1;
Stop := Pos(CR, Buffer);
if Stop = 0 then Stop := Length(Buffer) + 1;
while Start <= Length(Buffer) do
begin
(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Strings[(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Count - 1] :=
(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Strings[(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Count - 1] +
Copy(Buffer, Start, Stop - Start);
if Buffer[Stop] = CR then
begin
(tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Lines.Add('');
{$IFNDEF Linux}
SendMessage((tabPage.Pages[i].Controls[j] as
TframeClient).memoClient.Handle, WM_KEYDOWN, VK_UP, 1);
{$ENDIF}
end;
Start := Stop + 1;
if Start>Length(Buffer) then Break;
if Buffer[Start] = LF then Start := Start + 1;
Stop := Start;
while (Buffer[Stop] <>CR) and (Stop <= Length(Buffer)) do
Stop := Stop + 1;
Application.ProcessMessages;
end;
//????MSH if FStageConnect
then//-------------------------------------------------------
begin
// Access & Verify codes //????MSH check for
lock out...
if (pos('ACCESS CODE:', Buffer)>0) and FSites[i].FContinue
then
SendCommand(editAccess.Text + #9 + editVerify.Text,
(tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
// Signed on?
if pos('Not a valid ACCESS CODE/VERIFY CODE pair.', Buffer)>
0 then
begin
FSites[i].FContinue := FALSE;
ShowMessage(FSites[i].moniker + ': Not a valid ACCESS
CODE/VERIFY CODE pair.');
end;
//????MSH if pos('You last signed on', Buffer)>0 then
//????MSH KIDS;
end;
//------------------------------------------------
end;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.telnetClientStatus(ASender: TObject; const
AStatus: TIdStatus; const AStatusText: String);
begin
// Showmessage(ASender.ClassName); //TIdtelnet
{SetStatus Bar ???? with multiple connections?}
end;
//******************************************************************************
end.
 
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Quote
So far it seems to work for about 3 or less connections. On the fourth
and more, I start getting hangs. The application becomes unresponsive.
Occasionally it returns - only to become unresponsive again.

I am alittle unsure as to which way to go now.

I suspect my design is at fault, but it could simply be my
implementation. Am I being unrealistic? I am thinking that this
should be accomplishable for the magnitude(20-25) that I am looking at.
More info:
I tried changing this connecting to each in a serial manner and the
same hang occurs. The fourth connection hangs in the
HandleIncomingData procedure of the IdTelnet component.
I rearranged my servers to ensure it wasn't one specific server giving
me issues. I am not expecting any more data, so I can not determine why
it is{*word*154}.
Oh and I forgot to mention earlier>>Delphi 6 and indy 10.1.5 Indy
Components/10.0.52 source
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

XXXX@XXXXX.COM writes:
Quote
>So far it seems to work for about 3 or less connections. On the fourth
>and more, I start getting hangs. The application becomes unresponsive.
>Occasionally it returns - only to become unresponsive again.
>
>I am alittle unsure as to which way to go now.
>
>I suspect my design is at fault, but it could simply be my
>implementation. Am I being unrealistic? I am thinking that this
>should be accomplishable for the magnitude(20-25) that I am looking at.
More testing indicates actually that 5 connections are good and 7
connections are good, but even numbered connections over 2 are bad (4,
6, or 8). Weird. More testing...
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Hoi Jimmy
You need to repost your message on the Borland news server to make
everybody see it and possibly answer your message.
How to post to Delphi newsgroups:
<delphi.wikia.com/wiki/Delphi_Newsgroups>
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Riki -
Damn! I read these a lot, but first time really posting.
What a good reason for me to use Thunderbird. I have wanted to use it
- just too lazy to figure out how.
Now I need to use it - this is great!!!.
Thanks abunch. I was pretty clueless about my posting on Google.
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Delphi 6 and indy 10.1.5 Indy Components/10.0.52 source
I am posting this on Borland now - not Google... (Ugghhhh - now it'll be on google twice...)
I am trying to create an application that connects(telnet) to
multiple(8 to 20+) unix boxes and executes a list of several commands. (I will simplify the commands to buttons.)
The list of servers is loaded via an INI file. A TListbox and TIdTelnet are placed on a frame and are created dynamically on a TTabSheet(in a TPageControl) base on the number of entries in the INI file.
So far it seems to work for about 3 or less connections. On the fourth and more, I start getting hangs. The application becomes unresponsive. Occasionally it returns - only to become unresponsive again.
I am alittle unsure as to which way to go now.
I suspect my design is at fault, but it could simply be my
implementation. Am I being unrealistic? I am thinking that this
should be accomplishable for the magnitude(20-25) that I am looking at.
I haven't created threads for each dynamic tab - I assume some
threading is done in the component, but not sure exactly how that
playes into it. Do I need to create my own threads for each?
I'll post code so as to skip that request ;-) Thanks in advance for any help. Any streamlining/improvements on the component searching would be nice also. It is 380 lines or so including Francois Piette's demo code for the DataAvailable. I use long lines to(well past 80 sometimes) - sorry.
More info:
I tried changing this connecting to each in a serial manner and the same hang occurs. The fourth connection hangs in the
HandleIncomingData procedure of the IdTelnet component.
I rearranged my servers to ensure it wasn't one specific server giving me issues. I am not expecting any more data, so I can not determine why it is{*word*154}.
More testing indicates actually that 5 connections are good and 7
connections are good, but even numbered connections over 2 are bad (4, 6, or 8). Weird. Still{*word*154} in the HandleIncomingData procedure.
More testing...
unit fOctopus;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Menus, StdCtrls, ComCtrls, ToolWin, IniFiles, Dialogs, ImgList,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdTelnet,
fTelnet, ExtCtrls;
//******************************************************************************
type
TSite = record
site : string;
visn : string;
moniker : string;
id : string;
server : string;
port : integer;
FContinue : boolean;
FNext : boolean;
FConnected: boolean;
end;
//******************************************************************************
TfrmOctopus = class(TForm)
MainMenu1: TMainMenu;
statusBar: TStatusBar;
File1: TMenuItem;
Connection1: TMenuItem;
Help1: TMenuItem;
ToolBar1: TToolBar;
tbtnConnect: TToolButton;
tbtnButton1: TToolButton;
tbtnUpCarat: TToolButton;
ToolButton4: TToolButton;
tbtnKIDS: TToolButton;
ToolButton6: TToolButton;
ToolButton7: TToolButton;
imagesEnabled: TImageList;
imagesDisabled: TImageList;
tabPage: TPageControl;
pnlLeft: TPanel;
listboxSites: TListBox;
pnlAccessVerify: TPanel;
lblAccess: TLabel;
lblVerify: TLabel;
editAccess: TEdit;
editVerify: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Connect(i, j: integer);
procedure Disconnect(Sender: TObject);
//------------------------------------------------
procedure telnetClientDataAvailable(Sender: TIdTelnet; const Buffer: String);
procedure telnetClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
procedure tbtnUpCaratClick(Sender: TObject);
procedure tbtnConnectClick(Sender: TObject);
procedure tbtnKIDSClick(Sender: TObject);
procedure listboxSitesDrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
//------------------------------------------------
private
{ Private declarations }
FOctopusINI: TIniFile;
FSites: array of TSite;
FslSitelist: TStringlist;
FCursor: TCursor;
FStageConnect: boolean;
public
{ Public declarations }
procedure ImportINI;
procedure SetSites(i: integer);
procedure UpdateScreen(listboxToSendTo: TListbox);
procedure SendCommand(S: string; telnetToSendTo: TIdTelnet);
procedure KIDS;
end;
//******************************************************************************
var
frmOctopus: TfrmOctopus;
implementation
{$R *.dfm}
//******************************************************************************
procedure TfrmOctopus.ImportINI;
var
iniSectionName: string;
i: integer;
begin
listboxSites.Items.Clear;
FOctopusINI := TIniFile.Create(ExtractFilePath(ParamStr(0)) + 'Octopus.ini');
with FOctopusINI do
begin
FslSitelist := TStringlist.Create;
FslSitelist.Clear;
FOctopusINI.ReadSection('Sites', FslSitelist);
if (FslSitelist.Count>0) then
begin
SetLength(FSites, FslSitelist.Count);
for i := 0 to (FslSitelist.Count - 1) do
begin
iniSectionName := FOctopusINI.ReadString('Sites', 'site' + IntToStr(Ord(i)), '');
with FSites[i] do
begin
site := FOctopusINI.ReadString(iniSectionName, 'site', '');
visn := FOctopusINI.ReadString(iniSectionName, 'visn', '');
moniker := FOctopusINI.ReadString(iniSectionName, 'moniker', '');
id := FOctopusINI.ReadString(iniSectionName, 'id', '');
server := FOctopusINI.ReadString(iniSectionName, 'server', '');
port := FOctopusINI.ReadInteger(iniSectionName, 'port', 0);
FContinue := TRUE;
FNext := FALSE;
FConnected := FALSE;
end;
listboxSites.Items.Add(FSites[i].site);
end;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.SetSites(i: integer);
var
tabClients: TTabSheet;
frameClients: TframeClient;
begin
tabClients := TTabSheet.Create(tabPage);
tabClients.PageControl := tabPage;
tabClients.Caption := FSites[i].moniker;
tabClients.Tag := i;
frameClients := TframeClient.Create(tabClients);
frameClients.Parent := tabClients;
frameClients.Align := alClient;
frameClients.telnetClient.Host := FSites[i].server;
frameClients.telnetClient.OnStatus := telnetClientStatus;
frameClients.telnetClient.OnDataAvailable := telnetClientDataAvailable;
end;
//******************************************************************************
procedure TfrmOctopus.FormCreate(Sender: TObject);
var
i: integer;
begin
ImportINI;
for i := 0 to FslSiteList.Count - 1 do SetSites(i);
end;
//******************************************************************************
procedure TfrmOctopus.SendCommand(S: string; telnetToSendTo: TIdTelnet);
var
i: integer;
begin
for i := 1 to length(S) do telnetToSendTo.SendCh(S[i]);
telnetToSendTo.SendCh(#13);
end;
//******************************************************************************
procedure TfrmOctopus.Connect(i, j: integer);
var
s: string;
l: integer;
begin
(*
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
*)
if not((tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Connected) then
begin
(tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Connect;
l := tabPage.Pages[i].Tag;
tabPage.Pages[i].Tag := (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.TelnetThread.ThreadID;
s := UPPERCASE(FSites[l].moniker) + 'VISTA';
SendCommand(s, (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
end;
(*
end;
*)
end;
//******************************************************************************
procedure TfrmOctopus.Disconnect(Sender: TObject);
var
i, j: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if ((tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Connected) then
begin
(tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Disconnect;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.FormDestroy(Sender: TObject);
var
i, j: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if not((tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Connected) then
begin
if ((tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Connected) then
(tabPage.Pages[i].Controls[j] as TframeClient).telnetClient.Disconnect;
end;
end;
FslSitelist.Free;
FOctopusINI.Free;
end;
//******************************************************************************
procedure TfrmOctopus.UpdateScreen(listboxToSendTo: TListbox);
begin
if (listboxToSendTo.Items.Count>0) then
listboxToSendTo.TopIndex := listboxToSendTo.Items.Count - 1;
// Application.ProcessMessages;
end;
//******************************************************************************
procedure TfrmOctopus.telnetClientDataAvailable(Sender: TIdTelnet; const Buffer: String);
const
CR = #13;
LF = #10;
var
Start, Stop, iCurrentPID, i, j: Integer;
begin
iCurrentPID := Sender.TelnetThread.ThreadID;
for i := 0 to tabPage.PageCount - 1 do
begin
if tabPage.Pages[i].Tag = iCurrentPID then // Sync tab
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
begin // Find frame (only control, but for later...)
//------------------------------------------------
{This routine comes directly from the ICS TNDEMO code. Thanks to Francois Piette
It updates the memo control when we get data}
if ((tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Count = 0) then
(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Add('');
Start := 1;
Stop := Pos(CR, Buffer);
if Stop = 0 then Stop := Length(Buffer) + 1;
while Start <= Length(Buffer) do
begin
(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Strings[(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Count - 1] :=
(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Strings[(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Count - 1] +
Copy(Buffer, Start, Stop - Start);
if Buffer[Stop] = CR then
begin
(tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Items.Add('');
{$IFNDEF Linux}
SendMessage((tabPage.Pages[i].Controls[j] as TframeClient).listboxClient.Handle, WM_KEYDOWN, VK_UP, 1);
{$ENDIF}
end;
Start := Stop + 1;
if Start>Length(Buffer) then Break;
if Buffer[Start] = LF then Start := Start + 1;
Stop := Start;
while (Buffer[Stop] <>CR) and (Stop <= Length(Buffer)) do Stop := Stop + 1;
end;
//-------------------------------------------------------
if FStageConnect then
begin // Access & Verify codes
if (pos('ACCESS CODE:', Buffer)>0) and FSites[i].FContinue then
SendCommand(editAccess.Text + #9 + editVerify.Text, (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
if pos('Not a valid ACCESS CODE/VERIFY CODE pair.', Buffer)>0 then
begin // Signed on?
FSites[i].FContinue := FALSE;
listboxSites.Items[listboxSites.Items.IndexOf(FSites[i].site)] := '-' + FSites[i].site;
FSites[i].FConnected := FALSE;
if (i < tabPage.PageCount - 1) then Connect(i + 1, 0); // Connect singly.
if (i = tabPage.PageCount - 1) then Screen.Cursor := FCursor;
FSites[i].FNext := TRUE;
end;
if pos('You last signed on', Buffer)>0 then
begin // Signed on?
FSites[i].FNext := TRUE;
listboxSites.Items[listboxSites.Items.IndexOf(FSites[i].site)] := '+' + FSites[i].site;
FSites[i].FConnected := TRUE;
if (i < tabPage.PageCount - 1) then Connect(i + 1, 0); // Connect singly.
if (i = tabPage.PageCount - 1) then Screen.Cursor := FCursor;
end;
UpdateScreen((tabPage.Pages[i].Controls[j] as TframeClient).listboxClient);
end;
//-------------------------------------------------------
end;
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.telnetClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
begin
// Showmessage(AStatusText);
// Showmessage(ASender.ClassName); //TIdtelnet
// statusBar.Panels[0].Text := statusBar.Panels[0].Text + 'BOI' + '+ ';
// (ASender as TIdTelnet).TelnetThread.ThreadID
{SetStatus Bar}
end;
//******************************************************************************
procedure TfrmOctopus.tbtnUpCaratClick(Sender: TObject);
var
i, j:integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
SendCommand('^' + #13, (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
end;
end;
//******************************************************************************
procedure TfrmOctopus.tbtnConnectClick(Sender: TObject);
begin
FStageConnect := TRUE;
FCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
Connect(0, 0);
end;
//******************************************************************************
procedure TfrmOctopus.tbtnKIDSClick(Sender: TObject);
begin
FStageConnect := FALSE;
KIDS;
end;
//******************************************************************************
procedure TfrmOctopus.KIDS;
var
s: string;
i, j: integer;
begin
for i := 0 to tabPage.PageCount - 1 do
begin
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
if FSites[i].FConnected then
begin
SendCommand('Programmer Options', (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
SendCommand('KIDS', (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
SendCommand('Installation', (tabPage.Pages[i].Controls[j] as TframeClient).telnetClient);
end;
end;
end;
//******************************************************************************
procedure TfrmOctopus.listboxSitesDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
begin
try
with (Control as TListBox).Canvas do { draw on control canvas, not on the form }
begin
FillRect(Rect); { clear the rectangle }
if odSelected in State then
Font.Color := clWhite
else
begin
if (pos('+', listboxSites.Items[Index])>0) then Font.Color := clGreen
else if (pos('-', listboxSites.Items[Index])>0) then Font.Color := clMaroon
else Font.Color := clBlack;
end;
TextOut(Rect.Left + 2, Rect.Top, (Control as TListBox).Items[Index]); { display the text }
end;
except
MessageDlg('Access Violation Error Trap: On Draw.', mtError, [mbOK],0);
Application.Terminate;
end;
end;
//******************************************************************************
end.
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

"jimmydorsey" <XXXX@XXXXX.COM>writes
Quote
So far it seems to work for about 3 or less connections.
On the fourth and more, I start getting hangs.
Then you are likely not using it properly to begin with. The connections
are independant of each other.
Quote
The application becomes unresponsive.
Are you making blocking socket calls in the context of the main thread?
Indy is a blocking library, afterall.
Quote
I haven't created threads for each dynamic tab - I assume some
threading is done in the component, but not sure exactly how that
playes into it.
TIdTelnet does have an internal thread for reading all inbound data. So you
have to make sure that your code is thread-safe, and that you are not doing
any of your own reading, otherwise you will have race conditions on the
socket.
Quote
Any streamlining/improvements on the component searching would be nice
also.
The first thing you need to get rid of is all of the unnecessary looping.
You are wasting a lot of CPU cycles performing your lookups.
Quote
if not((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
The Connected() method is not thread-safe. It calls ReadFromSource(), which
is also called by CheckForDataOnSource() inside of TIdTelnet's internal
thread. ReadFromSource() is the only method that directly accesses the
socket and reads data from it into the IOHandler's InputBuffer. Having
multiple threads read from the socket at the same time can be very
problematic, and can potentially corrupt the InputBuffer's data.
Since your structure has its own FConnected member, I suggest you check that
instead, and just make sure that you keep it updated properly.
Quote
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connect;
You are not setting the FConnected member to True if Connect() succeeds.
Quote
l := tabPage.Pages[i].Tag;
tabPage.Pages[i].Tag := (tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.TelnetThread.ThreadID;
You should not be relying on Thread IDs, especially with Indy 10. Besides,
there is nothing in your code that needs that particular ThreadID anyway.
You already have the TIdTelnet pointer available as the Sender parameter of
the OnDataAvailable event handler. You could use the TIdTelnet's Tag
property to point back to its owner frame so that the event handler knows
which frame to access. Even better, you don't even need to do that much,
since the frame is (presumably) the Owner of the TIdTelnet, so you can use
its Owner property instead.
Quote
for j := 0 to tabPage.Pages[i].ControlCount - 1 do
Your TabSheet's only have 1 control on them - the frame - so there is no
need to loop through the child controls at all. There is only 1 available,
so you can index it directly.
Quote
if ((tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Connected) then
begin
(tabPage.Pages[i].Controls[j] as
TframeClient).telnetClient.Disconnect;
end;
You don't need to call Connected() before calling Disconnect().
Quote
procedure TfrmOctopus.FormDestroy(Sender: TObject);
You are duplicating the same code from Disconnect(). You could have
Destroy() simply call Disconnect() instead.
Quote
This routine comes directly from the ICS TNDEMO code.
Why are you using ICS demo code for Indy? They have nothing to do with each
other.
Quote
SendMessage((tabPage.Pages[i].Controls[j] as
TframeClient).listboxClient.Handle, WM_KEYDOWN, VK_UP, 1);
SendMessage() is a blocking function. I am assuming that you have the
TIdTelnet's ThreadedEvent property set to False (its default), is that
right? If so, then the OnDataAvailable event handlers for all of your
TIdTelnet instances are serialized. While one is running, all of the rest
are blocked. That could explain why your UI is unresposive all the time -
it is spending most of its time servicing Telnet data rather then user
input.
Since you are processing multiple sockets simultaneously, I'd suggest
that you set the ThreadedEvent property to True and then serialize only the
portions of the event handler code that directly access the main thread
objects. There is no need the serial the entire event handler code, since
most of it is local to each connection only.
Gambit
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Hoi Jimmy
Looking forward to see your postings on the Borland news server.
Doei Riki Wiki
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

I fixed all the little things. Thank you - Owner especially works great!!
Quote
Why are you using ICS demo code for Indy? They have nothing to do with each
other.
It was in the Telnet demo code for DataAvailable (Indy 9?)... It only formats the buffer so it is pretty in the listbox. It is just finding the CRs (#13). It does seem to work.
Quote
SendMessage() is a blocking function. I am assuming that you have the
TIdTelnet's ThreadedEvent property set to False (its default), is that
right? If so, then the OnDataAvailable event handlers for all of your
TIdTelnet instances are serialized. While one is running, all of the rest
are blocked. That could explain why your UI is unresposive all the time -
it is spending most of its time servicing Telnet data rather then user
input.
Yep - I had it set to FALSE, and that is probably exactly what is happening. I have changed it to TRUE because I do want these to be able to process in parallel without holding the interface hostage.
Quote
Since you are processing multiple sockets simultaneously, I'd suggest
that you set the ThreadedEvent property to True and then serialize only the
portions of the event handler code that directly access the main thread
objects. There is no need the serial the entire event handler code, since
most of it is local to each connection only.
I really, really, really want too, but it sound so hard... ;-) I don't understand how to identify, muchless serialize the main thread objects.
I am happy to hear the whole thing doesn't need serializing. My confusion comes in with all of these connections calling the same DataAvailable event in the main form. I assume that can happen without any problem?
I also assume that the issue is when I reference the main form objects (essentially - they are the main thread objects), correct?
How do I go about getting the "Data" that is "Available" to the main form? Is this where I have to get creative and do some kind of messaging cue and check/parse it periodically? Maybe launch threads from the DataAvailable? This piece of the puzzle is what I am really hung up on.
See - I am juggling 16 balls with only 2 hands and all 16 come back so fast I don't know how to handle them serially when I am not handling them initially in a serial manner. Not that I really could juggle 16 balls anyway, but I know my computer can...
Thanks again for your help so far.
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

"jimmydorsey" <XXXX@XXXXX.COM>writes
Quote
I really, really, really want too, but it sound so hard... ;-)
It is not hard at all. Use the TIdSync class.
Quote
I don't understand how to identify... the main thread objects.
All of your GUI components and private form members are operating in the
main thread.
Quote
My confusion comes in with all of these connections calling the same
DataAvailable event in the main form.
That will work just fine. Just make sure that its code is thread-safe,
meaning that multiple threads do not try to access the same data at the same
time.
Quote
How do I go about getting the "Data" that is "Available" to the main form?
Is this where I have to get creative and do some kind of messaging cue
and check/parse it periodically?
That is one option amongst several that are available.
For example (untested):
uses
IdSync;
type
TTelnetDataHelper = class(TIdSync)
protected
FBuffer: String;
procedure DoCheckUpdateConnectSites;
procedure DoAddBufferToItems;
procedure DoEnsureItems;
procedure DoNewItem;
public
Sheet: TTabSheet;
Frame: TFrameClient;
Telnet: TIdTelnet;
procedure AddBufferToItems(const ABuffer: String; int AStart,
ALength: Integer);
procedure EnsureItems;
procedure NewItem;
procedure CheckUpdateConnectSites(const ABuffer: String);
end;
procedure TTelnetDataHelper.AddBufferToItems(const ABuffer: String; int
AStart, ALength: Integer);
begin
FBuffer := Copy(ABuffer, AStart, ALength);
FThread.Synchronize(DoAddBufferToItems);
end;
procedure TTelnetDataHelper.DoAddBufferToItems;
begin
with Frame.ListBoxClient.Items do
Strings[Count - 1] := Strings[Count - 1] + FBuffer;
end;
procedure TTelnetDataHelper.CheckUpdateConnectSites(const ABuffer:
String);
begin
FBuffer := ABuffer;
FThread.Synchronize(DoCheckUpdateConnectSites);
end;
procedure TTelnetDataHelper.DoCheckUpdateConnectSites;
var
SiteIndex: Integer;
begin
SiteIndex := Sheet.PageIndex;
with frmOctopus do
begin
if FStageConnect then
begin
if (Pos('ACCESS CODE:', Self.FBuffer)>0) and
FSites[Index].FContinue then
SendCommand(editAccess.Text + #9 + editVerify.Text,
Self.Telnet);
if Pos('Not a valid ACCESS CODE/VERIFY CODE pair.',
Self.FBuffer)>0 then
begin
FSites[SiteIndex].FContinue := False;
FSites[SiteIndex].FConnected := False;
ListBoxSites.Items[ListBoxSites.Items.IndexOf(FSites[SiteIndex].site)] :=
'-' + FSites[SiteIndex].site;
if (SiteIndex < tabPage.PageCount - 1) then
Connect(SiteIndex + 1, 0);
if (SiteIndex = tabPage.PageCount - 1) then
Screen.Cursor := FCursor;
FSites[SiteIndex].FNext := True;
end;
if Pos('You last signed on', Self.FBuffer)>0 then
begin
FSites[SiteIndex].FNext := True;
FSites[SiteIndex].FConnected := True;
ListBoxSites.Items[ListBoxSites.Items.IndexOf(FSites[SiteIndex].site)] :=
'+' + FSites[SiteIndex].site;
if (SiteIndex < tabPage.PageCount - 1) then
Connect(SiteIndex + 1, 0);
if (SiteIndex = tabPage.PageCount - 1) then
Screen.Cursor := FCursor;
end;
UpdateScreen(Self.Frame.ListBoxClient);
end;
end;
end;
procedure TTelnetDataHelper.EnsureItems;
begin
FThread.Synchronize(DoEnsureItems);
end;
procedure TTelnetDataHelper.DoEnsureItems;
begin
with Frame.ListBoxClient.Items do
if Count = 0 then Add('');
end;
procedure TTelnetDataHelper.NewItem;
begin
FThread.Synchronize(DoNewItem);
end;
procedure TTelnetDataHelper.DoNewItem;
begin
with Frame.ListBoxClient do
begin
Items.Add('');
{$IFNDEF Linux}
PostMessage(Handle, WM_KEYDOWN, VK_UP, 1);
{$ENDIF}
end;
end;
procedure TfrmOctopus.telnetClientDataAvailable(Sender: TIdTelnet; const
Buffer: String);
var
Start, Stop: Integer;
begin
Helper := TTelnetDataHelper.Create;
try
Helper.Frame := TFrameClient(Sender.Owner);
Helper.Sheet := TTabSheet(Helper.Frame.Owner);
Helper.Telnet := Sender;
Helper.EnsureItems;
Start := 1;
Stop := Pos(CR, Buffer);
if Stop = 0 then Stop := Length(Buffer) + 1;
while Start <= Length(Buffer) do
begin
Helper.AddBufferToItems(Buffer, Start, Stop - Start);
if Buffer[Stop] = CR then Helper.NewItem;
Inc(Start);
if Start>Length(Buffer) then Break;
if Buffer[Start] = LF then Inc(Start);
Stop := Start;
while (Buffer[Stop] <>CR) and (Stop <= Length(Buffer)) do
Inc(Stop);
end;
Helper.CheckUpdateConnectSites(Buffer);
finally
Helper.Free;
end;
end;
Gambit
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

IdSync with "synchronize" was what I was missing. I read the nite I posted about threads and I am still just considering doing it all in threads.
Your solution has corrected my{*word*154} issues. However, I still have issues with it reporting the "DataAvailable" to the correct listbox. I have walked through it and as far as I can tell it should be reporting it correctly, but it isn't.
With all the separate calls I was getting mixed data in every window a little scrambled. So I rewrote the whole DataAvailable so that the whole call is in one procedure - thinking that atleast whole chunks would be returned together.
Indeed they were returned together, however, now I get 6 banners on the #6 connection (listbox) and banner 7 and 8 show up correctly. (The banners are just like FTP banners.)
Any ideas what might be causing this type of incorrect display. Note - everything is connecting - just not displaying correctly. That could just be luck and timing though.
I can thread it myself if you think it will fix my issue. Instead of IdSync, I will just thread all the DataAvailable responses. Don't know if that will really fix the issue though.
Any ideas?
Below is your code after I edited it for simplicity.
Thanks a bunch for the help.
[I really like the: "For example (untested):" Others must expect more that I do...]
I can not believe Gambit (a person whose post I read often) is answering my posts... Thanks again.
type
//******************************************************************************
TTelnetDataHelper = class(TIdSync)
protected
FBuffer: String;
procedure DoLoadDataAvailable;
public
Sheet: TTabSheet;
Frame: TFrameClient;
Telnet: TIdTelnet;
procedure LoadDataAvailable(const ABuffer: String);
end;
//******************************************************************************
procedure TfrmOctopus.telnetClientDataAvailable(Sender: TIdTelnet; const Buffer: String);
const
CR = #13;
LF = #10;
var
Helper: TTelnetDataHelper;
begin
Helper := TTelnetDataHelper.Create;
try
Helper.Frame := TFrameClient(Sender.Owner);
Helper.Sheet := TTabSheet(Helper.Frame.Owner);
Helper.Telnet := Sender;
Helper.LoadDataAvailable(Buffer);
finally
Helper.Free;
end;
end;
//******************************************************************************
procedure TTelnetDataHelper.LoadDataAvailable(const ABuffer: String);
begin
FBuffer := ABuffer;
FThread.Synchronize(DoLoadDataAvailable);
end;
//******************************************************************************
procedure TTelnetDataHelper.DoLoadDataAvailable;
const
CR = #13;
LF = #10;
var
Start, Stop: Integer;
begin
//-------------------------------------------------------
with Frame.ListBoxClient.Items do if Count = 0 then Add('');
Start := 1;
Stop := Pos(CR, FBuffer);
if (Stop = 0) then Stop := Length(FBuffer) + 1;
while (Start <= Length(FBuffer)) do
begin
with Frame.ListBoxClient.Items do
Strings[Count - 1] := Strings[Count - 1] + Copy(FBuffer, Start, Stop - Start);
if (FBuffer[Stop] = CR) then Frame.ListBoxClient.Items.Add('');
Start := Stop + 1;
if Start>Length(FBuffer) then Break;
if FBuffer[Start] = LF then Start := Start + 1;
Stop := Start;
while (FBuffer[Stop] <>CR) and (Stop <= Length(FBuffer)) do Stop := Stop + 1;
end;
//-------------------------------------------------------
end;
//******************************************************************************
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

"jimmydorsey" <XXXX@XXXXX.COM>writes
Quote
I still have issues with it reporting the "DataAvailable" to the correct
listbox.
That cnnot be happening. The DataAvailable() event handler specifies the
TIdTelnet instance that is triggering the event. The processing code is
accessing the TListBox that belongs to the TFrame that owns that TIdTelnet
instance. There is nothing in the code to access the wrong TListBox.
Quote
With all the separate calls I was getting mixed data in every window a
little
scrambled. So I rewrote the whole DataAvailable so that the whole call is
in
one procedure - thinking that atleast whole chunks would be returned
together.
You just defeated the whole purpose of using TIdSync. You reverted your
code back to serializing the entire event handler again, which was a major
cause of your earlier hangups.
Gambit
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Quote
That cnnot be happening.
It is happening. I could post screenshots. It is happening. Why - I don't know, but it is happening. I have stepped through the code too and agree it should not be happening, but it is. Did I mention - it is happening?
Quote
You just defeated the whole purpose of using TIdSync. You reverted your
code back to serializing the entire event handler again, which was a major
cause of your earlier hangups.
Actually - I didn't defeat the purpose of TIdSync - which was to have the individual threads report back to the main VCL thread. Both methods seem to work parially meaning the program does not hang. Even after the change. Though it may be flawed, it works more correctly than the previous code.
I talked to our network admin, and he seems to think it might be in the telnet specs themselveselves. Quite often he mentioned connecting to quickly (with automated responses) goofs up the responses back. Somtimes it comes back a little garbled or with multiple/duplicate lines and all sorts of garbage. He dod state that the connection is there correctly, but that the responses back do not always seem to come back correctly.
No response necessary. Thanks for your help. It did get me past a rock and a hard place...
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

"jimmydorsey" <XXXX@XXXXX.COM>writes
Quote
It is happening. I could post screenshots. It is happening. Why - I
don't know, but it is happening. I have stepped through the code too and
agree it should not be happening, but it is. Did I mention - it is
happening?
Quote
Actually - I didn't defeat the purpose of TIdSync
Yes, you did. The whole point of introducing TIdSync into your project was
NOT to serialize the entire contents of the event handler, only the portions
that needed to access the main thread. But, you changed the code to
serialize the entire contents again. If you do that, then you may as well
set the TIdTelnet's ThreadedEvent property back to False and get rid of the
TIdSync altogether, as you are not using it in a way that provide any help
at all.
Quote
which was to have the individual threads report back to the main VCL
thread.
That is NOT the main purpose of using TIdSync. It was meant to help
serialize only specific portions of the reporting code, not the entire
reporting code. There is a big difference there.
Gambit
 

Re:IdTelnet (Indy 10): Multi - Telnet sessions.

Quote
Yes, you did. The whole point of introducing TIdSync into your project was
NOT to serialize the entire contents of the event handler, only the portions
that needed to access the main thread. But, you changed the code to
serialize the entire contents again. If you do that, then you may as well
set the TIdTelnet's ThreadedEvent property back to False and get rid of the
TIdSync altogether, as you are not using it in a way that provide any help
at all.
I am really confused now because when I had ThreadedEvent set to FALSE it hung didn't seem to process for several minutes. The DataAvailable returns would bump into each other and not process. Now it processes in just a moment (admittedly with a superficial display error).
So even serializing the whole procedure adds beneficial functionality even if it is "less than" optimal and/or not as intended. Blocking is overcome period. I don't have to wait for one connection to finish or timeout before launching the next. The incoming which is multiple and at various response times for each connection is handled in a threaded method.
Quote
That is NOT the main purpose of using TIdSync. It was meant to help
serialize only specific portions of the reporting code, not the entire
reporting code. There is a big difference there.
There may or may not be a big difference (depends on your point of view and what you are trying to accomplish), but overcoming the blocking call does help reduce overhead time even if it is less than optimal. Also - it keeps the complete reponse together and it is processed as far as I can see more correctly.
I am still non-plused as to why your outlined code returned the results all garbled with missing CRs, interspersed characters and the like. I wonder if some of the code not in the TIdSync needs to be rethought. It fuctions correctly singly.
Well those are the results.