简体   繁体   English

使用TIdTCPServer进行多播(Indy 9 + Delphi 7)

[英]Multicasting with TIdTCPServer (Indy 9 + Delphi 7)

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. 我目前有一个带有OnExecute的TIdTCPServer,它读取传入的消息,将响应消息生成到线程安全队列中,然后调用一个函数来逐步执行队列并将消息发送回客户端。 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. 我的问题是OnExecute没有循环,因此在服务器从客户端收到消息之前,不会调用在队列上发送消息的调用。

Is there a way to get OnExecute to loop? 有没有办法让OnExecute循环? Or a way to trigger an OnExecute for a connection I know I have queued messages for? 或者为连接触发OnExecute的方法我知道我已将消息排队了?

My procedure TWinSocketSession object has a reference to the connection and includes the code for the outgoing message Queue. 我的过程TWinSocketSession对象具有对连接的引用,并包含传出消息Queue的代码。 It also has a procedure called SendMessages that steps through the queue and calls Connection.Write. 它还有一个名为SendMessages的过程,它遍历队列并调用Connection.Write。 Below is my OnExecute procedure. 下面是我的OnExecute程序。

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;

Yes, the TIdTCPServer.OnExecute event IS a looped event. 是的, TIdTCPServer.OnExecute事件一个循环事件。 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. 您的代码调用了ReadLn()并指定了25ms的超时,因此它不会阻止事件处理程序及时退出,因此它可以立即重新输入。 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. 如果您可以完全消除m_mWinSocketSession列表并且只是在OnConnect事件中将新队列直接分配给Connection,那么这将更好。 The OnDisconnect event can free the queue when the client disconnects. 当客户端断开连接时, OnDisconnect事件可以释放队列。

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM