TIdTCPServer
OnExecute
デフォルトで割り当てられたイベントハンドラーが必要です。これを回避するには、仮想メソッドから新しいクラスを派生させてオーバーライドする必要があります。また、呼び出すTIdTCPServer
仮想CheckOkToBeActive()
メソッドをオーバーライドする必要があります。それ以外の場合は、イベントハンドラーを割り当てて、を呼び出すようにします。DoExecute()
Sleep()
Sleep()
ただし、これは効果的な使用法ではありませんTIdTCPServer
。SendMessage()
より良い設計は、メソッドの内部から直接クライアントにアウトバウンドデータを書き込まないことです。エラーが発生しやすく(からの例外をキャッチしていない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;