Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2012-09

lnet component

12 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Antonio Fortuny

Hi list.

I'm looking for a solution to the next problem:
I've an application running in both Win32 and WinCE. So far it runs OK but I run into trouble when I try to control the TCP/IP traffic between the application and a TCP/IP server running into a Windows box (Delphi application).

AFAIK the TLTCPComponent (Lazarus lnet package) I use as a TCP client, runs asynchronously. But in some places I need to control a timeout and do something special when the TCP link fails on either the connection phase or the send/receive phase because the internal timeouts are definitely too long. I didn't see any place to setup a timeout for the TCP component.

For the moment I've built a loop using a Sleep (50 ms on each loop) and this is quite boring. I guess than when the main thread enters a Sleep, everyting in it falls into the same Sleep, TCP inclusive.

Question: how to suspend the application while waiting for either a connection or a response from the server ? In other words, how to run lnet in a synchronous way (like Indy does) ?

My own solution tends to use a thread which can run asynchronously and process event from the socket as they arrive (onConnect, onReceive). The thread can post a global variable when operation is done. The problrm is this is a heavy piece of code and tests are not easy. This also means an application refactory. Looks like Indy is working actually. Unfortunately, although Indy compiles and works with Lazarus Win32, Win64 and Linux64, it does not compile for WinCE.

All ideas are welcome.

Antonio.

Reply by email

Bernd
to Antonio Fortuny

2012/9/5 Antonio Fortuny :

Question: how to suspend the application while waiting for either a connection or a response from the server ? In other words, how to run lnet in a synchronous way (like Indy does) ?

I would use a TTimer and during the timer event check how far your handshake has proceeded or whether it got stuck somewhere and disconnect the TLTcp from within this timer event.

# Synchronous mode:

You can easily use LNet in synchronous mode, here is a code snippet that makes use of this, although I must say I have abandoned this particular piece of code, I would not do it the same way again, This code will establish many client connections to different servers and use a separate thread for each of them. If I were to do it again I would use only one network thread for all my TLTcp objects.

A TPeer object inherits from TThread, each TPeer represents exactly one outgoing connection. I removed some stuff that is not relevant, I hope I did not remove too much. CallAction is guaranteed to return after FConn.Timeout whether it is already connected or not, therefore I made my own timeout counter that simply counts how many times it returned:

constructor TPeer.Create(PeerAddrV4: String; PeerPort: Word);
begin
  inherited Create(true);
  FConn := TLTcp.Create(nil);
  FConn.OnDisconnect := @OnDisconnect;
  FConn.OnReceive := @OnReceive;
  FConn.OnError := @OnError;
  FConn.OnConnect := @OnConnect;
  log('trying %s:%d', [PeerAddrV4, PeerPort]);
  FConn.Connect(PeerAddrV4, PeerPort);
end;
destructor TPeer.Destroy;
begin
  FConn.Free;
  inherited Destroy;
end;
procedure TPeer.Execute;
var
  TimeoutCounter : Integer = 3; // ~seconds
begin
  FConn.Timeout := 950 + Random(100);
  repeat
    if (not FConn.Connected) and (TimeoutCounter = 0) then begin
      log('conection timeout (%s)', [PeerAddress]);
      FConn.Disconnect(True);
      Terminate;
    end else begin
      Dec(TimeoutCounter);
    end;
    try
      FConn.CallAction;
    except
      // this is not supposed to happen. No exception should
      // propagate up to this point. This must be a bug!
      LogException('BUG: during CallAction()');
    end;
    {$ifdef HEAPTRC}DumpHeap;{$endif}
  until Terminated;
  log('peer thread terminated (%s)', [PeerAddress]);
  FCanFree := True;
end;

If you want to have multiple TLTcp using the same thread then you must create only one Thread and one separate TLSelectEventer and assign this eventer to each of your TLTcp objects. Then you call the CallAction() method directly on the eventer itself instead of calling TLTcp.CallAction().

Its actually quite simple. LNet with its pluggable eventers is a brilliant piece of code (the core units at least) and its incredibly small and flexible :-)

Reply by email

Bernd
to Bernd

2012/9/5 Bernd :

If you want to have multiple TLTcp using the same thread then you must create only one Thread and one separate TLSelectEventer and assign this eventer to each of your TLTcp objects.

Note that when you are placing a TLTcp component on a form in the form designer then it will by default already have an eventer but it will be a very special class of eventer that is deeply tied into the LCL and generate its events from within the GUI event loop, you cannot call CallAction on this class of eventer. To make your own event loop you have to create a TLSelectEventer (or a TLEpollEventer on unix systems or a TLBestEventer on all systems) yourself and assign that eventer to the TLTcp. From then on you must use CallAction() in your own event loop. If you create a new TLTcp instance yourself (like in my above example) it will by default already have a TLBestEventer (either select or epoll) already created and assigned by default.

Reply by email

Antonio Fortuny
to Bernd: 2012/9/5 Antonio Fortuny : I would use a TTimer and during the timer event check how far…

Le 05/09/2012 11:05, Bernd a ?crit :

I would use a TTimer and during the timer event check how far your handshake has proceeded or whether it got stuck somewhere and disconnect the TLTcp from within this timer event.

My very first idea but tricky.

Show quoted text 11 lines
# Synchronous mode: You can easily use LNet in synchronous mode, here is a code snippet that makes use of this, although I must say I have abandoned this particular piece of code, I would not do it the same way again, This code will establish many client connections to different servers and use a separate thread for each of them. If I were to do it again I would use only one network thread for all my TLTcp objects.

OK, this confirms my opinion. I'll do it with a thread and use a semaphore set by the application and reset by the thread as soon as something usefull happens.
The thread will be suspended on TLTcp events to run and the application will be suspended during the semaphore lock. No Timers, full synchro, no fuzzy tricks

Thanks Bernd that's what I gonna implement, maybe not for the current modification but certainly for the next, in a few days.

Reply by email

Michael Schnell
to Antonio Fortuny: Hi list. I'm looking for a solution to the next problem: I've an application running in…
Antonio Fortuny wrote
. The problrm is this is a heavy piece of code and tests are not easy.

Not necessarily.

While the most efficient way indeed is to handle the communication greatly in the thread, your Thread also can just push the bytes in a TThreadList or something similar, and signal the MainThread by QueuAsyncCall().

Now the complex stuff can be handled (and debugged) in the MainThread, without the necessity to take care of mutual data access (such as necessary when accessing variables as well from the Thread as from the MainThread.

-Michael

Reply by email

Antonio Fortuny
to Michael Schnell

Le 05/09/2012 14:23, Michael Schnell a ?crit :

Show quoted text 5 lines
On 09/05/2012 11:35 AM, Antonio Fortuny wrote: > use a semaphore... You can't easily wait on a semaphore in the main thread. (In fact you can't wait at all.)

Sorry, bad word: TEvent instead as it allows a WaitForSingleObject I've already implemented TEvents between treads with success.

IMHO it's easy and straight-forward to use TApplication.QueueAsyncCall()

Not yet explored this but I will.

Show quoted text 11 lines
for this. -Michael., -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Antonio Fortuny
to Michael Schnell: Yep. IMHO using a thread for each TCP/IP Port (or serial interface or pipe or...) and…

Le 05/09/2012 14:13, Michael Schnell a ?crit :

Show quoted text 7 lines
On 09/05/2012 10:01 AM, Antonio Fortuny wrote: > > My own solution tends to use a thread yep. Yep. IMHO using a thread for each TCP/IP Port (or serial interface or pipe or...) and doing a blocking read is the only decent (and rather portable) way to handle an incoming data stream from those devices.

I agree

Show quoted text 10 lines
-Michael -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Antonio Fortuny
to Michael Schnell: Not necessarily. While the most efficient way indeed is to handle the communication…

Le 05/09/2012 14:19, Michael Schnell a ?crit :

On 09/05/2012 10:01 AM, Antonio Fortuny wrote: > . The problrm is this is a heavy piece of code and tests are not easy. Not necessarily.

Yes, maybe. But as I'm known to be a parano?d guy when tests and controls are concerned, it is a big piece of code actually, trust me.

Show quoted text 5 lines
While the most efficient way indeed is to handle the communication greatly in the thread, your Thread also can just push the bytes in a TThreadList or something similar, and signal the MainThread by QueuAsyncCall().

Maybe the most elegant solution. I'll test it for the new release.

Show quoted text 14 lines
Now the complex stuff can be handled (and debugged) in the MainThread, without the necessity to take care of mutual data access (such as necessary when accessing variables as well from the Thread as from the MainThread. -Michael -- _______________________________________________ Lazarus mailing list http://lists.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Michael Schnell
to Antonio Fortuny
Antonio Fortuny wrote
Maybe the most elegant solution. I'll test it for the new release.

This (and more) is what "AsyncPro" (formerly by TurboPower, now free on Sourceforge) does for Delphi with serial and TCP - ports.

With this the application programmer does not need to bother about threads.

Unfortunately there is no port for Lazarus, yet.

-Michael

Reply by email

Bernd
to Michael Schnell: You can't easily wait on a semaphore in the main thread. (In fact you can't wait at all.)…

2012/9/5 Michael Schnell :

On 09/05/2012 11:35 AM, Antonio Fortuny wrote: IMHO it's easy and straight-forward to use TApplication.QueueAsyncCall() for this.

I have recently implemented something that has one network thread (using LNet and events that are fired and running from within this network thread) and once they have received a complete protocol message from the network they instantiate and enqueue one of my TMsgXxxx (my own proprietary classes of messages) into a thread safe queue and then wake up the main thread who will then pop messages from the queue and execute them from within the main thread.

My message classes all implement an Execute() method, the Execute() method will be called from the main thread once it has popped the message from the queue and can interact with the gui. This was a lot of infrastructure code but it works nicely and I can use this message queue also for other messages that need to be sent to the GUI, not only network messages, I can essentially send a piece of code along with data to the gui for later execution.

the complete code is here:
https://github.com/prof7bit/TorChat/tree/torchat2/src/core

TLTcp server, the eventer an the event thread is initialized in tc_client.pas TLTcp clients are created in tc_buddy.pas (they are using socks4a to connect), incoming data is parsed and and enqueued in tc_conn.pas the queue and message base classes are in tc_msgqueue.pas and tc_protocol.pas and then there are a lot of protocol messages that implement my IProtocolMessage interface and are able to do funny things from within the main thread in their Execute() method.

The entry point for the GUI thread is the Pump() method of TTorChatClient, this will pop the next message, execute it, check a few timeouts and then immediately return.

Reply by email