TCP server without OnExecute event

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

Send / receive message is not a problem; I like it:

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; 

Note 1: If I do not use OnExecute, the program throws an exception when the client connects.
Note 2: If I use OnExecute without any action, then the processor load will be equal to% 100 Note 3: I have no way to change TCP clients.

So what should I do?

+4
source share
5 answers

Use OnExecute, and if you have nothing to do, Sleep () for a certain period of time, say, 10 milliseconds. Each connection has its own OnExecute handler, so this affects only each individual connection.

+4
source

TIdTCPServer requires the TIdTCPServer event handler. To get around this, you must get a new class from TIdTCPServer and override its virtual CheckOkToBeActive() method, as well as override the virtual DoExecute() to call Sleep() . Otherwise, just assign an event handler and call it Sleep() .

This is not an effective use of TIdTCPServer . It’s best not to write outgoing data to clients directly from your SendMessage() method. Not only is it error-prone (you do not catch exceptions from WriteBuffer() ) and block SendMessage() while writing, but you also serialize your messages (client 2 cannot receive data until client 1 does this earlier). A more efficient design is to provide each client with its own inbound outbound queue, and then SendMessage() place data in each client queue as necessary. You can then use the OnExecute event to check each client queue and perform the actual recording. Thus, SendMessage() no longer blocked, less error prone, and clients can be written in parallel (as it 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; 
+7
source

The Indy component is designed to emulate blocking during a network connection. You must encapsulate all your code in the OnExecute event OnExecute . This should be simpler because most protocols are blocked in any way (send a command, wait for a response, etc.).

You don't seem to like the mode of operation, you need something that works without blocking. You should consider using a component kit that has been designed for how you intend to use it: try ICS ! ICS does not use threads; all work is done in event handlers.

+1
source

In the OnExecute handler, you can use thread-binding methods, such as TEvent and TMonitor, to wait until there is data for the client.

TMonitor is available since Delphi 2009 and provides methods (Wait, Pulse and PulseAll) for sending / receiving notifications with minimal CPU usage.

+1
source

I had a similar situation with a 100% processor, and it was resolved by adding IdThreadComponent and:

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

Is it correct? I'm not sure.

+1
source

Source: https://habr.com/ru/post/1396983/


All Articles