我想创建一个TCPserver,并根据需要向客户端发送/接收消息()--,而不是TCPserver的OnExecute事件。
发送/接收消息不是问题,我喜欢这样:
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客户端。
那我该怎么办?
发布于 2012-02-17 08:24:08
使用OnExecute,如果您没有什么可做的,那么睡眠()一段时间,比如说10毫秒。每个连接都有自己的OnExecute处理程序,因此这只会影响每个单独的连接。
发布于 2012-02-17 23:41:04
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; 发布于 2012-02-17 10:07:52
Indy组件集旨在模拟网络连接上的阻塞操作。您应该将所有代码封装在OnExecute事件处理程序中。这应该更容易,因为大多数协议都阻塞任何方式(发送命令、等待响应等)。
你显然不喜欢它的运作方式,你会喜欢没有阻塞的东西。您应该考虑使用为您打算使用它的方式而设计的组件套件:尝试一下科技中心套件!ICS不使用线程,所有工作都是在事件处理程序中完成的。
https://stackoverflow.com/questions/9324723
复制相似问题