TCPserver without OnExecute event

后端 未结 5 1021
情歌与酒
情歌与酒 2021-01-06 04:20

I want to make a TCPserver and send/receive message to clients as needed, not OnExecute event of the TCPserver.

Send/receive message is not a probl

5条回答
  •  礼貌的吻别
    2021-01-06 04:40

    TIdTCPServer requires an OnExecute event handler assigned by default. To get around that, you would have to derive a new class from TIdTCPServer and override its virtual CheckOkToBeActive() method, and should also override the virtual DoExecute() to call Sleep(). Otherwise, just assign an event handler and have it call Sleep().

    This is not an effective use of TIdTCPServer, though. A better design is to not write your outbound data to clients from inside of your SendMessage() method directly. Not only is that error-prone (you are not catching exceptions from WriteBuffer()) and blocks SendMessage() during writing, but it also serializes your communications (client 2 cannot receive data until client 1 does first). A much more effective design is to give each client its own thread-safe outbound queue, and then have SendMessage() put the data into each client's queue as needed. You can then use the OnExecute event to check each client's queue and do the actual writing. This way, SendMessage() does not get blocked anymore, is less error-prone, and clients can be written to in parallel (like they should be).

    Try something like this:

    uses
      ..., IdThreadSafe;
    
    type
      TMyContext = class(TIdServerContext)
      private
        FQueue: TIdThreadSafeStringList;
        FEvent: TEvent;
      public
        constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
        destructor Destroy; override;
        procedure AddMsgToQueue(const Msg: String);
        function GetQueuedMsgs: TStrings;
      end;
    
    constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
    begin
      inherited;
      FQueue := TIdThreadSafeStringList.Create;
      FEvent := TEvent.Create(nil, True, False, '');
    end;
    
    destructor TMyContext.Destroy;
    begin
      FQueue.Free;
      FEvent.Free;
      inherited;
    end;
    
    procedure TMyContext.AddMsgToQueue(const Msg: String);
    begin
      with FQueue.Lock do
      try
        Add(Msg);
        FEvent.SetEvent;
      finally
        FQueue.Unlock;
      end;
    end;
    
    function TMyContext.GetQueuedMsgs: TStrings;
    var
      List: TStringList;
    begin
      Result := nil;
      if FEvent.WaitFor(1000) <> wrSignaled then Exit;
      List := FQueue.Lock;
      try
        if List.Count > 0 then
        begin
          Result := TStringList.Create;
          try
            Result.Assign(List);
            List.Clear;
          except
            Result.Free;
            raise;
          end;
        end;
        FEvent.ResetEvent;
      finally
        FQueue.Unlock;
      end;
    end;
    
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
      TCPServer.ContextClass := TMyContext;
    end; 
    
    procedure TFormMain.TCPServerExecute(AContext: TIdContext);
    var
      List: TStrings;
      I: Integer;
    begin
      List := TMyContext(AContext).GetQueuedMsgs;
      if List = nil then Exit;
      try
        for I := 0 to List.Count-1 do
          AContext.Connection.IOHandler.Write(List[I]);
      finally
        List.Free;
      end;
    end;
    
    procedure TFormMain.SendMessage(const IP, Msg: string); 
    var 
      I: Integer; 
    begin 
      with TCPServer.Contexts.LockList do 
      try 
        for I := 0 to Count-1 do 
        begin
          with TMyContext(Items[I]) do
          begin
            if Binding.PeerIP = IP then 
            begin 
              AddMsgToQueue(Msg); 
              Break; 
            end;
          end; 
        end;
      finally 
        TCPServer.Contexts.UnlockList; 
      end; 
    end; 
    

提交回复
热议问题