Background
I'm trying to write a server application which CAN be
thought of as a chat program in some regards. When first
arriving in the client application, you see what appears to
be a chat system. There are different "rooms" which the
client can enter. Now these aren't really chat rooms, but it
is a nice analogy. So when a user enters a "room" I am
setting a parameter in a modified TSimpleClient that
identifies the room they are in. Once all of the clients
have entered the room, I want to have a stateful, controlled
process of events occur. So at the point the room task
begins, I am creating a room controller object. This object
gets passed a copy of the clients in the task's room, as
well as the TCPServer. The client's "chat window", gets
replaced by a standard GUI form. The task controller then
gets "executed", and begins looping through the task list
that it has been setup to perform. So far so good. Most of
these will send commands to the client applications, which
they interpret and use to display data and update the GUI.
Now comes the bad part. At several points in the task lists,
I need to be able to get information from the clients.
Mostly, this is working good as well. However, there are
times when the server needs the clients to respond in a
particular order, and not ask client #2 for a response until
client #1 has responded, (as client #2 may modify their
response based on the response of client #1). SO to
accomplish this, on of the task actions will send a command
to client #1, (let's call this command GetResponse).
GetResponse will take information coming in, and present it
to client #1, and wait for a response from client #1. Then
it will do the same for each client in turn.
Now I was originally using this construct
thisResponse := '';
while thisResponse = '' do begin
thisResponse
:=ReadResponseFromClient(currentClient);
end;
{then do some processing and go to the next client}
Where ReadResponseFromClient used the passed index to
find the TSimpleClient in the controllers list and then do a
ReadLn on the client. This would work a few times, but after
going to several clients it would stop and the thisResponse
variable was always containing ''. So then I replaced the
above by using ReadLnWait. This didn't help either. After a
lot of head scratching I finally figured out what was going
on. In the TCPServer.Execute, there was a readLn as well,
and this was getting the buffer contents before the
controller's ReadResponseFromClient could get to it. Now
what I can't figure out why this works sometimes, (always
for the first client), and THEN stops working, but that
doesn't seem totally revelant to making it stop.
So now I'm scratching my head trying to figure out how to
get this to work. I've come up with a couple ideas, none of
which seem very good to me. Idea #1: Have the controller
object create it's own TCPServer, using a different port,
and somehow pass this information to the client app, and
have it make a new connection to the new server.
Idea #2: Add additional code in the current
TCPServer.Execute that will bypass the ReadLn that is there.
Now what I don't know yet is whether doing this will allow
the buffer to "pass-through" to the controller's ReadLn or
not.
Idea #3: Have the controller override the TCPServer.Execute,
and only call inherited if the PeerThread is a member of the
controller's task room client list. I just thought of this
as I was typing, so I haven't considered why this is a bad
idea.
Idea #4: Scrap the whole conceptual framework and do it all
in a different way.
Another problem I can see coming is that I want each
controller object to be created in it's own thread, (so that
one controller in it's execute loop won't prevent other
controller's from running their loops as well). My knowledge
of what you can and can't do with threads is somewhat
limited, but I have my doubts as to my ability to easily
communicate from the Server applications main thread to the
controller threads. This was part of Idea #6 where I was
going to let the server portion put the client response into
a variable, which the controller object(s) (threads), could
then access via a critical section or some other thread safe
manner.
Anyway, if anyone has stuck with this this long, please tell
me your ideas and comments on this, as I'm severely
depressed by this failure, and just really have no idea as
to how to proceeed. I don't want to blow another week's
effort only to hit another roadblock and have to scrap the
whole thing then, so any ideas at all will be greatly
appreciated.
Thanks for reading
Windopaene
This is not really Winsock, but more 'real-time multiuser, mutlitasking
design'. You have to do some design for this app. Readlns in threads and
VCL is not good. Your data seems like it could do with some organisation.
You have clients & rooms. Clients can be outside or in any one room. Once
in a room, clients can engage in a protocol with other client within the
room. The protocol allows for multiple bindings - a client may be
communicating with more than one client at once, conforming to some 'task
rules'.
Start with the data. Work out what entities you need & what fields belong
to what entity Then the thread/process design. The code writes itself
then, (LOL).
I do not think that anyone could scetch out a design for your app. without
something like a requirement/functional spec :)
Rgds,
Martin
Actually it is very good design to use threads and ReadLn. It abstracts your communication
code.
--
Chad Z. Hower (Kudzu) - http://www.pbe.com/Kudzu/
Current Location: St. Petersburg, Russia
"Programming is an art form that fights back"
In contention in two different threads? Naww...
Martin :)
Well, conceptual debates about the merits of using threads and readln
aside, any ideas as to how to solve my problem or comments of the
efficacy of the various solutions I'm considering?
one solution would be the use of a TCPServer and n TCPClient. These talk
to each other via ReadLn(with a time out of x milliseconds).
So when your controllerobjects encounters a waitcondition, than you
allow den server to read x times for y periode. When no usefull data
arrived throw an error and enter an error protocol.
When useful data arrived (non empty string) -> check for right content
of the string. If wrong data -> reread or if the complete time frame was
done -> throw an error. Enter an error protocol.
In other words let indy handle TCP/IP and programm the whole logic
within the execute. Create Command Objects which are able to decode
encode and verify strings. Create your Controllogic Objects. Create the
Error and Keep Alive Objects. Than test the uml design. Than implement.
enjoy,
Ron.
Intersting thought about adding an "error protocol" type handling. Not
sure it directly applies here, but it might be workable for certain
aspects that I haven't fully addressed yet. Thanks
It depends what the contention is. You can easily write from one thread and read from another.
You can even read from multiple threads on a single socket if you do it correctly.