Discussion:
Sending an unsolicited message from Indy server
(too old to reply)
D-Fan
2008-07-17 05:31:25 UTC
Permalink
I have an Indy client application and an indy server application. There
are occasions that I want to send an unsolicited message from the server
to the client. This is done so that the client isn't constantly polling
the server to check the value of a flag. I want to send the client an
indicator that it can check for to see if the server has some data for
it. If the client finds that the flag is set then it will query the
server for the messages. My question is do I need to use a tcpclient
component on the server to do this unsolicited communication? It seems
like each client needs to have a tcpserver to receive this communication
since this message can be sent at any time. It doesn't seem feasible to
use the existing (primary) tcpserver component to send an unsolicited
message to the client PC.
Jamie Dale
2008-07-17 14:00:20 UTC
Permalink
D-Fan,

Just setup a reading thread for your TCPClient.

Have it loop frequently to ReadLn() and see if there are any new messages
sent. If so, process them..

At the server end you just need to send the message to the correct AThread
or Context as and when you need to. The client will be looping through its
reading thread and will automatically read it when the message is sent to
it.

No need for a server at the client end ;)

Hope that helps
Post by D-Fan
I have an Indy client application and an indy server application. There
are occasions that I want to send an unsolicited message from the server to
the client. This is done so that the client isn't constantly polling the
server to check the value of a flag. I want to send the client an
indicator that it can check for to see if the server has some data for it.
If the client finds that the flag is set then it will query the server for
the messages. My question is do I need to use a tcpclient component on the
server to do this unsolicited communication? It seems like each client
needs to have a tcpserver to receive this communication since this message
can be sent at any time. It doesn't seem feasible to use the existing
(primary) tcpserver component to send an unsolicited message to the client
PC.
D-Fan
2008-07-17 15:36:41 UTC
Permalink
Post by Jamie Dale
D-Fan,
Just setup a reading thread for your TCPClient.
Have it loop frequently to ReadLn() and see if there are any new messages
sent. If so, process them..
At the server end you just need to send the message to the correct AThread
or Context as and when you need to. The client will be looping through its
reading thread and will automatically read it when the message is sent to
it.
No need for a server at the client end ;)
Hope that helps
Post by D-Fan
I have an Indy client application and an indy server application. There
are occasions that I want to send an unsolicited message from the server to
the client. This is done so that the client isn't constantly polling the
server to check the value of a flag. I want to send the client an
indicator that it can check for to see if the server has some data for it.
If the client finds that the flag is set then it will query the server for
the messages. My question is do I need to use a tcpclient component on the
server to do this unsolicited communication? It seems like each client
needs to have a tcpserver to receive this communication since this message
can be sent at any time. It doesn't seem feasible to use the existing
(primary) tcpserver component to send an unsolicited message to the client
PC.
I can't seem to write to a client from a server unless I am in the
execute event of a server. This gives me exposure to the
Acontext.iohandler where I can converse with the client. This means
that the client has to initiate the request. In order to reduce the
traffix across the network I want the server to initiate the request by
sending a certain flag (value) to the client.
Jamie Dale
2008-07-17 17:02:57 UTC
Permalink
I can't seem to write to a client from a server unless I am in the execute
event of a server.
Thats because you haven't looped through your list of clients/contexts which
IdTCPServer holds.

In Indy 9 you can use something like this:
var

List: TList;

I: Integer;

begin

List := IdTCPServer1.Contexts.LockList;

For I := 0 to List.Count -1 do

begin

If TIdContext(List.Items[I]).Binding.PeerIP = {IP of client or other
criteria to match} then

begin

TIdContext(List.Items[I]).Connection.IOHandler.WriteLn('Command
here');

end;

end;

That will make the server write to the selected connection - Which will be
read at the other end by the Reading thread which is looping (on a timeout
to prevent errors when destroying the thread). Timeout can be used like
this:

IdTCPClient1.IOHandler.ReadLn('', 500);
500 is the milliseconds to wait - 1000 is 1 second.
I recommend always using a timeout - especially while in a thread as it can
cause problems if you want to update thread properties or destroy it etc. If
you want infinate time out just use ReadLn; - EG No () or parameters.

If you want to use a Timeout but don't want to execute code for nothing
then:
//Read: String;
Read := IdTCPClient1.IOHandler.ReadLn('', 500);
If Read <>'' then
begin
//Do stuff here...
end;
This gives me exposure to the Acontext.iohandler where I can converse
with the client. This means that the client has to initiate the request.
It only looks like that because not much is mentioned in the Indy docs on
how to initiate communication from the server to the client. If you follow
the example above you will see you can access any AContext outside of the
execute handler.
In order to reduce the traffix across the network I want the server to
initiate the request by sending a certain flag (value) to the client.
Thats one way of doing it and it is certainly simpler but should you ever
develop your own protocol to which other developers have access then you are
relying on them to poll your server and stick to your protocol standards. If
you send commands directly from your server then they are forced to read all
comms and react accordingly.

You can also keep your own list of clients when a new client connects -
Thats a thought for the future...

Hope that helps!
Jamie Dale
2008-07-17 17:11:39 UTC
Permalink
My bad, that should be 'Indy 10'

Soz..
D-Fan
2008-07-17 17:25:00 UTC
Permalink
Post by Jamie Dale
My bad, that should be 'Indy 10'
Soz..
This will probably solve my problem and allow me to stick to the single
server component that I initially wanted. Thanks for your help.
Jamie Dale
2008-07-17 17:35:02 UTC
Permalink
Post by D-Fan
Post by Jamie Dale
My bad, that should be 'Indy 10'
Soz..
This will probably solve my problem and allow me to stick to the single
server component that I initially wanted. Thanks for your help.
No problem!

Forgot to add this line of code though - You should always unlock() the
servers context list once you're done with it and also free the List:

IdTCPServer1.Contexts.UnLockList;

List.Free;

Sorry I fogot!
Remy Lebeau (TeamB)
2008-07-17 18:55:14 UTC
Permalink
You should always unlock() the servers context list once
IdTCPServer1.Contexts.UnLockList;
List.Free;
DO NOT FREE THE LIST!!!! It is owned by the TThreadList for the Contexts
property, which in turn is owned by the TIdTCPServer.


Gambit
Jamie Dale
2008-07-17 20:20:07 UTC
Permalink
Post by Remy Lebeau (TeamB)
You should always unlock() the servers context list once
IdTCPServer1.Contexts.UnLockList;
List.Free;
DO NOT FREE THE LIST!!!! It is owned by the TThreadList for the Contexts
property, which in turn is owned by the TIdTCPServer.
Heh, learn something new every day...

I had wondered about this in the past but found example code somewhere that
did free it so stuck with it. Strangely it always seems to work too!

I'd better go and edit that line out of my code....

cheers remy
Remy Lebeau (TeamB)
2008-07-17 20:56:51 UTC
Permalink
Post by Jamie Dale
I had wondered about this in the past but found example code
somewhere that did free it
Not any of the code I've every posted. Such code would sure cause random
AVs after the first unsolicted message is sent.
Post by Jamie Dale
Strangely it always seems to work too!
I seriously doubt that. You are freeing a list that TIdTCPServer uses
internally for its connection management.


Gambit
Jamie Dale
2008-07-17 21:19:07 UTC
Permalink
Post by Remy Lebeau (TeamB)
Not any of the code I've every posted. Such code would sure cause random
AVs after the first unsolicted message is sent.
I never said that! Remy we all know you're a better coder than that!

Its sample source I remember seeing on the web once ages ago.
Remy Lebeau (TeamB)
2008-07-17 18:53:58 UTC
Permalink
Post by Jamie Dale
TIdContext(List.Items[I]).Connection.IOHandler.WriteLn('Command
here');
You need to implement a separate lock for the connection so the OnExecute
handler can't write its own data at the same time. Otherwise, the data from
the two threads (the one firing OnExecute, and the one looping through the
Contexts list) can overlap, corrupting the communication.


Gambit
Remy Lebeau (TeamB)
2008-07-17 18:51:48 UTC
Permalink
Post by D-Fan
I can't seem to write to a client from a server unless
I am in the execute event of a server.
Yes, you can. Simply lock the server's Contexts list, find the desired
connection in it, and write your data to it. You just have to make sure you
do the writing in a thread-safe manner so as not to overlap with any data
the OnExecute handler tries to write at the same time.
Post by D-Fan
This gives me exposure to the Acontext.iohandler where I can
converse with the client. This means that the client has to initiate
the request.
No, it does not.


Gambit
Remy Lebeau (TeamB)
2008-07-17 18:50:05 UTC
Permalink
Post by Jamie Dale
Just setup a reading thread for your TCPClient.
It is more complicated than just that.


Gambit
Remy Lebeau (TeamB)
2008-07-17 18:49:34 UTC
Permalink
I have an Indy client application and an indy server application. There
are occasions that I want to send an unsolicited message
from the server to the client.
Please go to http://www.deja.com and search the newsgroup archives. This
has been discussed many times before.
do I need to use a tcpclient component on the server to do
this unsolicited communication?
No. You can send the message using the existing connection. But you have
to redesign your protocol at the data level to be asynchronous in order to
support such messages, and you have to use outbound queues on the server
side so unsolicated messages do not overlap replies to client commands, and
you can't use SendCmd() on the client side anymore since it may receive an
unsolicated message instead of a command reply.
It seems like each client needs to have a tcpserver to receive
this communication since this message can be sent at any time.
No, it does not. That would just lead to problems with firewalls and
routers anyway.
It doesn't seem feasible to use the existing (primary) tcpserver component
to send an unsolicited message to the client PC.
It is feasible, if implemented properly.


Gambit
D-Fan
2008-07-18 17:29:39 UTC
Permalink
When I finish my coding can I get you to review it for a fee?
Post by Remy Lebeau (TeamB)
I have an Indy client application and an indy server application. There
are occasions that I want to send an unsolicited message
from the server to the client.
Please go to http://www.deja.com and search the newsgroup archives. This
has been discussed many times before.
do I need to use a tcpclient component on the server to do
this unsolicited communication?
No. You can send the message using the existing connection. But you have
to redesign your protocol at the data level to be asynchronous in order to
support such messages, and you have to use outbound queues on the server
side so unsolicated messages do not overlap replies to client commands, and
you can't use SendCmd() on the client side anymore since it may receive an
unsolicated message instead of a command reply.
It seems like each client needs to have a tcpserver to receive
this communication since this message can be sent at any time.
No, it does not. That would just lead to problems with firewalls and
routers anyway.
It doesn't seem feasible to use the existing (primary) tcpserver component
to send an unsolicited message to the client PC.
It is feasible, if implemented properly.
Gambit
Jamie Dale
2008-07-18 22:33:12 UTC
Permalink
Post by D-Fan
When I finish my coding can I get you to review it for a fee?
Remy does offer consulting - Check out his site:
http://www.lebeausoftware.org/consulting.aspx


@Remy... That right click message is a pain. You have no idea the lengths I
went to so I could copy and paste that URL to get you some possible
business...
Remy Lebeau (TeamB)
2008-07-18 23:08:45 UTC
Permalink
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
My website's pages are not meant to be viewed outside of the main frameset.
You don't have to send people to the particular pages directly, just send
them to the main URL. They'll see the Consulting link on the sidebar.


Gambit
Jamie Dale
2008-07-19 12:29:00 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
My website's pages are not meant to be viewed outside of the main
frameset. You don't have to send people to the particular pages directly,
just send them to the main URL. They'll see the Consulting link on the
sidebar.
Fair point but you could just use redirect code to redirect the browser to
the frames instead of that annoying right click message.

Eventually I just used View>Source and got the page name for the menu. Went
there, View>Source and got the page name for consulting.

Besides, many people like to go straight to the page with the info they
want - They don't like having to hunt around (or the thought of it).
Admittedly your site is clean and clear but my point still stands that some
people don't like the thought of a paper chase.
Chris Ueberall
2008-07-19 17:55:48 UTC
Permalink
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
I don't know why you are complaining. Remy's homepage is straight
forward, and using the right tools allows aou to grab the URLs without
any effort. I guess you used IE, which shows that annoying message you
mentioned when js is enabled. I guess Remy used that simple 'disableIE'
method to force the use of alternative browsers...
When you are using the IE7 you just need to drag the link into a new
tab, to get the URL ready for copying.

--
Jamie Dale
2008-07-19 21:29:10 UTC
Permalink
Post by Chris Ueberall
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
I don't know why you are complaining.
I wasn't really complaining - it was just general feedback.

Yes I also agree that Remys site is straight forward but someone asked a
question about whether Remy would review their project for a fee. I simply
wanted to help out and point them to Remys page.

regards
D-Fan
2008-07-19 21:30:05 UTC
Permalink
Post by Jamie Dale
Post by Remy Lebeau (TeamB)
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
My website's pages are not meant to be viewed outside of the main
frameset. You don't have to send people to the particular pages directly,
just send them to the main URL. They'll see the Consulting link on the
sidebar.
Fair point but you could just use redirect code to redirect the browser to
the frames instead of that annoying right click message.
Eventually I just used View>Source and got the page name for the menu. Went
there, View>Source and got the page name for consulting.
Besides, many people like to go straight to the page with the info they
want - They don't like having to hunt around (or the thought of it).
Admittedly your site is clean and clear but my point still stands that some
people don't like the thought of a paper chase.
Why is all of this crap being attached to this thread. I just wanted to
get information on technical resources to review my code.
Jamie Dale
2008-07-19 22:47:35 UTC
Permalink
Post by D-Fan
Why is all of this crap being attached to this thread. I just wanted to
get information on technical resources to review my code.
All this 'crap' is attached to this thread as originally it was to help you
;)
D-Fan
2008-07-19 21:33:39 UTC
Permalink
Post by Jamie Dale
Post by Remy Lebeau (TeamB)
Post by Jamie Dale
@Remy... That right click message is a pain. You have no
idea the lengths I went to so I could copy and paste that URL
to get you some possible business...
My website's pages are not meant to be viewed outside of the main
frameset. You don't have to send people to the particular pages directly,
just send them to the main URL. They'll see the Consulting link on the
sidebar.
Fair point but you could just use redirect code to redirect the browser to
the frames instead of that annoying right click message.
Eventually I just used View>Source and got the page name for the menu. Went
there, View>Source and got the page name for consulting.
Besides, many people like to go straight to the page with the info they
want - They don't like having to hunt around (or the thought of it).
Admittedly your site is clean and clear but my point still stands that some
people don't like the thought of a paper chase.
Anyway, thanks for the link. If I had known about their services I
would have sent a small project to them initially. Anyway, I am
learning something.
Remy Lebeau (TeamB)
2008-07-20 07:35:41 UTC
Permalink
Post by Jamie Dale
Fair point but you could just use redirect code to redirect the
browser to the frames instead of that annoying right click message.
Not reliably. Believe me, I worked as a webmaster for several years. I
know the tricks to do things like that, and the shortcomings they are
limited by.


Gambit

Loading...