Reputation: 23
I know that what I describe below is not technically multicasting, but I lack a better description of what I am trying to do.
I currently have a TIdTCPServer with an OnExecute that reads the incoming message, generates the response messages into a thread safe queue and then calls a function to step through the queue and send the messages back to the client. This part of the code is working and allows clients to request sets of data.
When a client changes an object on the server, I want all of the clients notified of this change. The code currently adds the notification message to each queue for each connection. My problem is that OnExecute is not looped, so the call to send messages on the queue is not called until the Server receives a message from the Client.
Is there a way to get OnExecute to loop? Or a way to trigger an OnExecute for a connection I know I have queued messages for?
My procedure TWinSocketSession object has a reference to the connection and includes the code for the outgoing message Queue. It also has a procedure called SendMessages that steps through the queue and calls Connection.Write. Below is my OnExecute procedure.
procedure TWinServerSession.IdTCPServer1Execute(AThread: TIdPeerThread);
Var
i: Integer;
strMessage: String;
begin
//find the Socket Session for connection
for i := 0 to m_usClientCount do
begin
if m_mWinSocketSession[i]<>Nil then
if ( m_mWinSocketSession[i].Connection = Athread.Connection ) then break;
end;
//read the message
strMessage := m_mWinSocketSession[i].Connection.ReadLn(#0,25,-1);
//parse the message and populate the Queue with outgoing messages
m_mWinSocketSession[i].ParseInString(strMessage);
//send all of the outgoing messages
m_mWinSocketSession[i].SendMessages;
end;
Upvotes: 1
Views: 2287
Reputation: 595349
Yes, the TIdTCPServer.OnExecute
event IS a looped event. It is NOT triggered when an inbound message arrives. It is triggered in a continuous loop for the lifetime of the connection, regardless of what the connection is actually doing. It is the event handler's resposibility to implement blocking behavior as needed. Your code is calling ReadLn()
with a 25ms timeout specified, so it will not block the event handler from exiting in a timely manner so it can re-enter itself immediately. If your event handler is being blocked from exiting in a timely manner, then you have a deadlock issue elsewhere in your code.
I would suggest the following changes, however:
procedure TWinServerSession.IdTCPServer1Connect(AThread: TIdPeerThread);
var
i: Integer;
begin
for i := 0 to m_usClientCount do
begin
if (m_mWinSocketSession[i] = nil) then
begin
m_mWinSocketSession[i] := TWinSocketSession.Create;
m_mWinSocketSession[i].Connection := AThread.Connection;
// for easier access in the other events...
AThread.Data := m_mWinSocketSession[i];
Exit;
end;
end;
// cannot start a new session
AThread.Connection.Disconnect;
end;
procedure TWinServerSession.IdTCPServer1Disconnect(AThread: TIdPeerThread);
var
session: TWinSocketSession;
i: Integer;
begin
session := TWinSocketSession(AThread.Data);
AThread.Data := nil;
if session <> nil then
begin
for i := 0 to m_usClientCount do
begin
if m_mWinSocketSession[i] = session then
begin
m_mWinSocketSession[i] := nil;
Break;
end;
end;
session.Free;
end;
end;
procedure TWinServerSession.IdTCPServer1Execute(AThread: TIdPeerThread);
var
session: TWinSocketSession;
strMessage: String;
begin
session := TWinSocketSession(AThread.Data);
//read a message
strMessage := AThread.Connection.ReadLn(#0, 25, -1);
if not AThread.Connection.ReadLnTimedOut then
begin
//parse the message and populate the Queue with outgoing messages
session.ParseInString(strMessage);
end;
//send all of the outgoing messages
session.SendMessages;
end;
This would work even better if you can eliminate the m_mWinSocketSession list completely and just assign a new queue directly to the Connection in the OnConnect
event. The OnDisconnect
event can
free the queue when the client disconnects.
procedure TWinServerSession.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
AThread.Data := TWinSocketSession.Create;
TWinSocketSession(AThread.Data).Connection := AThread.Connection;
end;
procedure TWinServerSession.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
session := TWinSocketSession(AThread.Data);
AThread.Data := nil;
session.Free;
end;
Upvotes: 3