I started to experiment with Indy (looking for a worthy successor to port some legacy TServerSocket-TClientSocket apps to). As Emba had some issues with Indy lately I decided to wipe it off, pulling an up-to-date version from https://github.com/IndySockets/Indy/ and installing that one. I have a deadlock issue with my hello world app, where it seems that TidIOHandler.ReadFromSource does not return and my project throws a "disconnected" exception upon exiting. Setup is fairly simple: TidTCPServer with defaultport set to 1024, TidTCPClient with host set to 127.0.0.1 and port set to 1024. procedure TForm1 . Button1Click ( Sender : TObject ); begin IdTCPServer1 . Active := True ; IdTCPClient1 . Connect ; IdTCPClient1 . IOHandler . WriteLn ( 'Hello, world' ); end ; procedure TForm1 . IdTCPServer1Execute ( AContext : TIdContext ); s : String ; begin s := AContext . Binding . PeerIP + ': ' + AContext . Connection . IOHandler . ReadLn ; TThread . Queue ( nil , procedure Begin Memo1 . Lines . Add ( s ) End ); end ; TCPServerExecute get hits twice. First, it reads "Hello, world" and puts it to the memo correctly. The second time, it goes into the method mentioned above and never returns (can not debug the exact location, as my Delphi is showing the line indicator somewhere random...). Again, this is my first test run of Indy (copied the code from https://stackoverflow.com/questions/31039580/how-can-i-send-and-recieve-strings-from-tidtcpclient-and-tidtcpserver-and-to-cr ) and have zero knowledge in the component yet. Am I doing something wrong? What would be the smallest, least complicated solution for me so I can start my learning curve? if not IdTCPServer1 . Active then begin IdTCPServer1 . Active := True ; IdTCPClient1 . Connect ; end ; IdTCPClient1 . IOHandler . WriteLn ( 'Hello, world' ); I did not find projects IndyCore260, IndyProtocols260 etc. for the new Delphi 10.3.3 in the library on GitHub. @Kryvich Thank you for the suggestion, but that will only get rid of an exception "Already connected" if the button is pressed twice during the lifetime of the application. I know it's not a nice and error handling code, it was meant as a test run only. Emba broke Indy's SSL certificate handling somehow (see: https://community.idera.com/developer-tools/b/blog/posts/rad-studio-10-3-3-indy-server-ssl-certificate-patch ) and despite having a patch I feel more comfortable using a more up-to-date source. There are no 260, only 250 packages, but they compile and install nicely on 10.3.3. procedure TForm1 . Button1Click ( Sender : TObject ); begin if not IdTCPServer1 . Active then begin IdTCPServer1 . Active := True ; IdTCPClient1 . Connect ; end ; Inc ( ClickCount ); IdTCPClient1 . IOHandler . WriteLn ( Format ( 'Hello, world. Click #%d.' , [ ClickCount ])); end ; procedure TForm1 . FormCloseQuery ( Sender : TObject ; var CanClose : Boolean ); begin if IdTCPServer1 . Active then IdTCPClient1 . Disconnect ; end ; procedure TForm1 . IdTCPServer1Execute ( AContext : TIdContext ); s : string ; begin s := AContext . Binding . PeerIP + ': ' + AContext . Connection . IOHandler . ReadLn ; TThread . Queue ( nil , procedure begin Memo1 . Lines . Add ( s ) end ); end ; if IdTCPServer1 . Active then begin IdTCPServer1 . Active := False ; IdTCPClient1 . Disconnect ; end ; You'll get the exception. Then swap the lines: IdTCPClient1 . Disconnect ; IdTCPServer1 . Active := False ; No exception. Your client is sending only 1 line of text.  The server's OnExecute event is looped, and so it tries to read 2 lines.  There is no data for the 2nd read is receive, so it blocks the calling thread waiting for new data that will never arrive, until the client disconnects or the server is deactivated.  At which time, the blocked read will then fail and raise a socket exception. Let it happen that way! The server will handle the exception for you and stop the thread that called the OnExecute event.  You will see the exception only if you are running your code in the IDE's debugger (and have not configured the debugger to ignore the exception), or have assigned a handler to the server's OnException event. To help illustrate this better, you should move your client's IOHandler.Write() call into a button OnClick event, and then press it multiple times while the TIdTCPClient is connected to the server, like Kryvich described (though I would have used separate buttons for Connect, Disconnect, and Write()).  You will see the ReadLn() call unblock with each button click. If the former, then the server's OnConnect and OnDisconnect events will usually suffice.  If you do not assign any component to the server's Scheduler property, a TIdSchedulerOfThreadDefault component is created and assigned for you automatically when you activate the server.  TIdSchedulerOfThreadDefault creates a new thread per connection, and frees each thread when the connections are closed.  Thus the server's OnConnect and OnDisconnect events are called only once per thread. If the latter, then as connections are closed and threads get reused, the OnConnect and OnDisconnect events can be called for multiple connections on the same thread, so that wouldn't really be a good place to perform per-thread initializations/cleanups (per-connection, yes).  A better option ( at least for now ) is to derive a new class from TIdThreadWithTask and have it override the virtual BeforeExecute() and AfterExecute() methods to perform per-thread initializations/cleanups as needed, and then you can explicitly assign a TIdSchedulerOfThreadPool component to the server's Scheduler property, and assign your Task class to the TIdSchedulerOfThreadPool.ThreadClass property.