Domanda

Voglio fare un tcpserver e invio / ricezione messaggi ai clienti se necessario , non evento OnExecute del tcpserver.

Invia / Ricevi messaggio non è un problema; Mi piace che:

procedure TFormMain.SendMessage(IP, Msg: string);
var
  I: Integer;
begin
  with TCPServer.Contexts.LockList do
  try
    for I := 0 to Count-1 do
      if TIdContext(Items[I]).Connection.Socket.Binding.PeerIP = IP then
      begin
        TIdContext(Items[I]).Connection.IOHandler.WriteBuffer(Msg[1], Length(Msg));
        //  and/or Read 
        Break;
      end;
  finally
    TCPServer.Contexts.UnlockList;
  end;
end;

Nota 1:. Se io non uso OnExecute, il rilancio del programma un'eccezione quando un client si connette
Nota 2: Se io uso OnExecute senza fare nulla, l'utilizzo della CPU va al% 100 | Nota 3:. Non ho la possibilità di cambiare i client TCP

Che cosa devo fare?

È stato utile?

Soluzione

Usa OnExecute e se non hai niente da fare, Sleep () per un periodo di tempo, diciamo 10 millisecondi. Ogni connessione ha il proprio gestore OnExecute quindi questo effetto solo ogni singola connessione.

Altri suggerimenti

TIdTCPServer richiede un gestore di eventi OnExecute assegnato per impostazione predefinita. Per ovviare a questo, si dovrà derivare una nuova classe da TIdTCPServer e ignorare il suo metodo CheckOkToBeActive() virtuale, e dovrebbe anche ignorare l'DoExecute() virtuale chiamata Sleep(). In caso contrario, basta assegnare un gestore di eventi e lo hanno chiamata Sleep().

Non si tratta di un uso efficace delle TIdTCPServer, però. Un design migliore è quello di non scrivere i dati in uscita verso i clienti all'interno del vostro metodo di SendMessage() direttamente. Non solo è che soggetto a errori (che non stanno recuperando eccezioni WriteBuffer()) e blocca SendMessage() durante la scrittura, ma anche serializza le tue comunicazione (client 2 non possono ricevere dati fino a quando il client 1 fa prima). Un design molto più efficace è quello di dare ad ogni cliente la propria coda in uscita thread-safe, e quindi avere SendMessage() inserire i dati nella coda di ogni cliente, se necessario. È quindi possibile utilizzare l'evento OnExecute per controllare la coda di ogni cliente e fare la scrittura vera e propria. In questo modo, SendMessage() non viene bloccato più, è meno soggetto a errori, e clienti può essere scritto in parallelo (come dovrebbero essere).

provare qualcosa di simile:

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; 

Nel gestore OnExecute, è possibile utilizzare i metodi di comunicazione filo come tID e TMonitor aspettare fino a quando non ci sono dati per il cliente.

TMonitor è disponibile dal Delphi 2009 e fornisce i metodi (Wait, Pulse e PulseAll) per inviare / ricevere le notifiche con l'utilizzo della CPU mininmal.

Il set componente Indy è progettato per emulare blocco operazione su una connessione di rete. Si suppone di incapsulare tutto il codice nel gestore di eventi OnExecute. Che dovrebbe essere più facile , perché la maggior parte dei protocolli stanno bloccando qualsiasi modo (comando di invio, di attesa per la risposta, ecc).

a quanto pare non mi piace di modalità di funzionamento, vuoi qualcosa che funziona senza bloccare. Si dovrebbe considerare l'utilizzo di una suite di componenti che è progettato per il modo in cui si intende utilizzarlo: dare la ICS suite di una prova! ICS non fa uso di fili, tutto il lavoro è fatto in gestori di eventi.

Ho avuto simile situazione prendendo il 100% della CPU ed è risolto con l'aggiunta di IdThreadComponent e:

void __fastcall TForm3::IdThreadComponent1Run(TIdThreadComponent *Sender)
{
    Sleep(10);
}

E 'giusto? Non sono sicuro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top