dudup
asked on
Simple client/server application continued
Hi,
I took the example that was made by TheRealLoki from this question :
https://www.experts-exchange.com/questions/21842452/Simple-client-server-application.html
How do I enumerate (get information) of all connected clients ??
Suppose I want to write the _ID of all connected clients.
Another strange thing is, I put "Label2" in the fServerMain form.
Then I changed two of the events, added this line :
Label2.Caption := IntToStr(IdTCPServer1.Thre ads.LockLi st.Count);
I just want to print the number of connected clients.
procedure TfServerMain.IdTCPServer1C onnect(ATh read: TIdPeerThread);
var
ID_: string;
begin
Label2.Caption := IntToStr(IdTCPServer1.Thre ads.LockLi st.Count);
ID_ := AThread.Connection.Socket. Binding.Pe erIP + ':' + IntToStr(AThread.Connectio n.Socket.B inding.Pee rPort);
AThread.Data := TClientSpecificData.Create WithSettin gs(ID_);
LogMessage(lmtInformation, ldNone, ID_ + ' Connected');
end;
procedure TfServerMain.IdTCPServer1D isconnect( AThread: TIdPeerThread);
begin
if assigned(AThread.Data) then
begin
(AThread.Data as TClientSpecificData).Free;
AThread.Data := nil;
end;
Label2.Caption := IntToStr(IdTCPServer1.Thre ads.LockLi st.Count);
LogMessage(lmtInformation, ldNone, AThread.Connection.Socket. Binding.Pe erIP + ':' + IntToStr(AThread.Connectio n.Socket.B inding.Pee rPort) + ' Disconnected');
end;
But then, the server got stuck after a client got disconnected.
Any idea why this happen ?
Thanks in advance.
I took the example that was made by TheRealLoki from this question :
https://www.experts-exchange.com/questions/21842452/Simple-client-server-application.html
How do I enumerate (get information) of all connected clients ??
Suppose I want to write the _ID of all connected clients.
Another strange thing is, I put "Label2" in the fServerMain form.
Then I changed two of the events, added this line :
Label2.Caption := IntToStr(IdTCPServer1.Thre
I just want to print the number of connected clients.
procedure TfServerMain.IdTCPServer1C
var
ID_: string;
begin
Label2.Caption := IntToStr(IdTCPServer1.Thre
ID_ := AThread.Connection.Socket.
AThread.Data := TClientSpecificData.Create
LogMessage(lmtInformation,
end;
procedure TfServerMain.IdTCPServer1D
begin
if assigned(AThread.Data) then
begin
(AThread.Data as TClientSpecificData).Free;
AThread.Data := nil;
end;
Label2.Caption := IntToStr(IdTCPServer1.Thre
LogMessage(lmtInformation,
end;
But then, the server got stuck after a client got disconnected.
Any idea why this happen ?
Thanks in advance.
ASKER
So, inside the execute thread we may not access the GUI in the main form. If we want to do this, we have to post message and capture it from the main thread.
Because later on, I would like to display progress bar for every process that are submitted by the clients :
CLIENT_1: 10%
CLIENT_2: 15%
CLIENT_3: Done
CLIENT_4: 95%
To do this, inside the execute thread, I will check the progress and then post message to the main form. Catch the message, and update the progress bar list.
Is this a good plan ?
Multithreading is also new to me :)
Because later on, I would like to display progress bar for every process that are submitted by the clients :
CLIENT_1: 10%
CLIENT_2: 15%
CLIENT_3: Done
CLIENT_4: 95%
To do this, inside the execute thread, I will check the progress and then post message to the main form. Catch the message, and update the progress bar list.
Is this a good plan ?
Multithreading is also new to me :)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
btw, if you are using Indy 9 change the "DisplayConnectionCount" procedure to
procedure TfServerMain.DisplayConnec tionCount( var Msg: TMessage);
var
i: integer;
FoundDisconnection: boolean;
begin
FoundDisconnection := True;
with IdTCPServer1.Threads.LockL ist do
try
lbConnections.Items.BeginU pdate;
lbConnections.Items.Clear;
lConnectionCount.Caption := IntToStr(Count) + ' connections';
for i := 0 to Count - 1 do
begin
if assigned( TIdPeerThread(Items[i]).Da ta) then
lbConnections.Items.Add( TClientSpecificData(TIdPee rThread(It ems[i]).Da ta).ID)
else
begin
lbConnections.Items.Add( 'Disconnecting...' );
FoundDisconnection := True;
end;
end;
finally
lbConnections.Items.EndUpd ate;
IdTCPServer1.Threads.UnLoc kList;
end;
if FoundDisconnection then
PostMessage(fServerMain.Ha ndle, WM_DisplayConnectionCount, 0, 0);
end;
procedure TfServerMain.DisplayConnec
var
i: integer;
FoundDisconnection: boolean;
begin
FoundDisconnection := True;
with IdTCPServer1.Threads.LockL
try
lbConnections.Items.BeginU
lbConnections.Items.Clear;
lConnectionCount.Caption := IntToStr(Count) + ' connections';
for i := 0 to Count - 1 do
begin
if assigned( TIdPeerThread(Items[i]).Da
lbConnections.Items.Add( TClientSpecificData(TIdPee
else
begin
lbConnections.Items.Add( 'Disconnecting...' );
FoundDisconnection := True;
end;
end;
finally
lbConnections.Items.EndUpd
IdTCPServer1.Threads.UnLoc
end;
if FoundDisconnection then
PostMessage(fServerMain.Ha
end;
oops, top line should read
FoundDisconnection := False;
FoundDisconnection := False;
ASKER
Loki, could you please posted the source and form of the server part ?
If not, please the complete server Execute procedure. I cant get it to work.
Thanks
If not, please the complete server Execute procedure. I cant get it to work.
Thanks
ASKER
Here is my server execute that is taken from your demo :
procedure TfServerMain.IdTCPServer1E xecute(ATh read: TIdPeerThread);
var
i: integer;
S, S2: string;
InCmd: string;
begin
// send the ID command and the user's unique "ID"
try
// Main Command Loop
while AThread.Connection.Connect ed do
begin
// check if user is logged in
InCmd := ReceiveStringWithLogging(A Thread);
case (AThread.Data as TClientSpecificData).Clien tStage of
csNone:
begin //LOGIN username password
if (pos('LOGIN', uppercase(InCmd)) = 1) then CMD_LOGIN(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else SendStringWithLogging(AThr ead, 'ERROR Not logged in. Can not use "' + InCmd +'" command');
end;
csLoggedIn:
begin
if InCmd = 'PING' then SendStringWithLogging(AThr ead, 'PONG') //Note: we do not show logging for this, becaue we want it as fast as possible
else if InCmd = 'JUMP' then SendStringWithLogging(AThr ead, 'Whee!')
else if (pos('TIMER ', InCmd) = 1) then CMD_TIMER(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if (pos('COUNTDOWN ', InCmd) = 1) then CMD_COUNTDOWN(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if InCmd = 'FILE' then CMD_FILE(AThread)
else if (pos('PROCESS ', InCmd) = 1) then CMD_PROCESS(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if InCmd = 'QUIT' then AThread.Connection.Disconn ect
else SendStringWithLogging(AThr ead, 'ERROR Unknown command "' + InCmd +'"');
end; // of ClientStage = csLoggedIn
end; // of case notlogged in, or logged in
end; // of while doing commands loop
except
on e: EIdSocketError do
begin
if pos('10053', E.Message) > 0 then
ThreadLogMessage(lmtInform ation, ldNone, 'Client disconnected')
else
ThreadLogMessage(lmtError, ldNone, E.Message);
end;
on e: exception do
begin
if pos('CONNECTION CLOSED GRACEFULLY', uppercase(e.Message)) > 0 then
ThreadLogMessage(lmtInform ation, ldNone, 'Client disconnected gracefully')
else
ThreadLogMessage(lmtError, ldNone, E.Message);
end;
end;
end;
procedure TfServerMain.IdTCPServer1E
var
i: integer;
S, S2: string;
InCmd: string;
begin
// send the ID command and the user's unique "ID"
try
// Main Command Loop
while AThread.Connection.Connect
begin
// check if user is logged in
InCmd := ReceiveStringWithLogging(A
case (AThread.Data as TClientSpecificData).Clien
csNone:
begin //LOGIN username password
if (pos('LOGIN', uppercase(InCmd)) = 1) then CMD_LOGIN(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else SendStringWithLogging(AThr
end;
csLoggedIn:
begin
if InCmd = 'PING' then SendStringWithLogging(AThr
else if InCmd = 'JUMP' then SendStringWithLogging(AThr
else if (pos('TIMER ', InCmd) = 1) then CMD_TIMER(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if (pos('COUNTDOWN ', InCmd) = 1) then CMD_COUNTDOWN(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if InCmd = 'FILE' then CMD_FILE(AThread)
else if (pos('PROCESS ', InCmd) = 1) then CMD_PROCESS(AThread, copy(InCmd, pos(' ', InCmd) + 1, maxint))
else if InCmd = 'QUIT' then AThread.Connection.Disconn
else SendStringWithLogging(AThr
end; // of ClientStage = csLoggedIn
end; // of case notlogged in, or logged in
end; // of while doing commands loop
except
on e: EIdSocketError do
begin
if pos('10053', E.Message) > 0 then
ThreadLogMessage(lmtInform
else
ThreadLogMessage(lmtError,
end;
on e: exception do
begin
if pos('CONNECTION CLOSED GRACEFULLY', uppercase(e.Message)) > 0 then
ThreadLogMessage(lmtInform
else
ThreadLogMessage(lmtError,
end;
end;
end;
you are missing the "file progress" parts
begin
AThread.Connection.Tag := integer(AThread.Data); //store our data object here also, so that the file progress knows a bit more about us
AThread.Connection.OnWorkB egin := (AThread.Data as TClientSpecificData).TCPCl ientInside ThreadWork Begin;
AThread.Connection.OnWork := (AThread.Data as TClientSpecificData).TCPCl ientInside ThreadWork ;
AThread.Connection.OnWorkE nd := (AThread.Data as TClientSpecificData).TCPCl ientInside ThreadWork End;
apart from that, it's identical to my execute method
begin
AThread.Connection.Tag := integer(AThread.Data); //store our data object here also, so that the file progress knows a bit more about us
AThread.Connection.OnWorkB
AThread.Connection.OnWork := (AThread.Data as TClientSpecificData).TCPCl
AThread.Connection.OnWorkE
apart from that, it's identical to my execute method
I've put the server and the client code on sourceforge for you
https://sourceforge.net/projects/internetdemos/
https://sourceforge.net/projects/internetdemos/
ASKER
Thanks Loki!
you should also use a message to update the count, it is not wise to use events
here's the ocde you need, i probably should have thought of it and put it in the main demo i wrote for you
I put a TListbox (lbConnections) and a TLabel (lConnectionCount) on the server form
WM_DisplayConnectionCount = WM_user + 101;
Procedure DisplayConnectionCount(var
Procedure TfServerMain.DisplayConnec
var
i: integer;
begin
with IdTCPServer1.Contexts.Lock
try
lbConnections.Items.BeginU
lbConnections.Items.Clear;
lConnectionCount.Caption := IntToStr(Count) + ' connections';
for i := 0 to Count - 1 do
begin
if assigned(TIdContext(Items[
lbConnections.Items.Add( TClientSpecificData(TIdCon
else
lbConnections.Items.Add( 'new' );
end;
finally
lbConnections.Items.EndUpd
IdTCPServer1.Contexts.UnLo
end;
end;
procedure TfServerMain.IdTCPServer1C
var
ID_: string;
begin
ID_ := AContext.Connection.Socket
AContext.Data := TClientSpecificData.Create
LogMessage(lmtInformation,
PostMessage(fServerMain.Ha
end;
procedure TfServerMain.IdTCPServer1D
begin
if assigned(AContext.Data) then
begin
(AContext.Data as TClientSpecificData).Free;
AContext.Data := nil;
end;
LogMessage(lmtInformation,
PostMessage(fServerMain.Ha
end;