4

TCPserverの OnExecute イベントではなく、 TCPserver を作成し、必要に応じてクライアントにメッセージを送受信したい。

メッセージの送受信は問題ありません。私はそれが好きです:

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;

注 1: OnExecute を使用しないと、クライアントの接続時にプログラムが例外を発生させます。
注 2: 何もせずに OnExecute を使用すると、CPU 使用率が %100
になります。 注 3: TCP クライアントを変更する機会がありません。

それで、私は何をすべきですか?

4

5 に答える 5

7

TIdTCPServerOnExecuteデフォルトで割り当てられたイベントハンドラーが必要です。これを回避するには、仮想メソッドから新しいクラスを派生させてオーバーライドする必要があります。また、呼び出すTIdTCPServer仮想CheckOkToBeActive()メソッドをオーバーライドする必要があります。それ以外の場合は、イベントハンドラーを割り当てて、を呼び出すようにします。DoExecute()Sleep()Sleep()

ただし、これは効果的な使用法ではありませんTIdTCPServerSendMessage()より良い設計は、メソッドの内部から直接クライアントにアウトバウンドデータを書き込まないことです。エラーが発生しやすく(からの例外をキャッチしていないWriteBuffer())、書き込み中にブロックするSendMessage()だけでなく、通信をシリアル化します(クライアント2はクライアント1が最初に受信するまでデータを受信できません)。より効果的な設計は、各クライアントに独自のスレッドセーフなアウトバウンドキューをSendMessage()提供し、必要に応じてデータを各クライアントのキューに配置することです。次に、OnExecuteイベントを使用して各クライアントのキューを確認し、実際の書き込みを行うことができます。このようにSendMessage()して、ブロックされなくなり、エラーが発生しにくくなり、クライアントを並列に書き込むことができます(本来あるべきように)。

次のようなものを試してください。

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; 
于 2012-02-17T23:41:04.107 に答える
4

OnExecuteを使用し、何もすることがない場合は、一定期間、たとえば10ミリ秒の間Sleep()を実行します。各接続には独自のOnExecuteハンドラーがあるため、これは個々の接続にのみ影響します。

于 2012-02-17T08:24:08.293 に答える
1

OnExecuteハンドラーでは、TEventやTMonitorなどのスレッド通信メソッドを使用して、クライアントのデータが得られるまで待機できます。

TMonitorはDelphi2009以降で使用可能であり、CPU使用率を最小限に抑えて通知を送受信するためのメソッド(Wait、Pulse、PulseAll)を提供します。

于 2012-02-17T11:48:08.537 に答える
1

私は100%のCPUを使用する同様の状況にあり、IdThreadComponentを追加することで解決しました:

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

そうですか?私はわかりません。

于 2012-05-28T03:48:13.033 に答える
1

コンポーネント セットは、ネットワーク接続でのブロック操作をエミュIndyレートするように設計されています。すべてのコードをイベント ハンドラーにカプセル化する必要があります。ほとんどのプロトコルは何らかの方法でブロックされているため (コマンドの送信、応答の待機など)、これは簡単なはずです。OnExecute

どうやらその操作モードが気に入らないようです。ブロックせずに機能するものが必要です。使用方法に合わせて設計されたコンポーネント スイートの使用を検討する必要があります。ICSスイートを試してみてください。ICS はスレッドを使用せず、すべての作業はイベント ハンドラーで行われます。

于 2012-02-17T10:07:52.420 に答える