I use Indy 10.6.2.5298.
What is the difference of TIdTCPConnection.Disconnect and TIdIOHandler.Close? Both of them disconnect the line but sometimes the former makes an access violation error.
I am sorry that I can't understand it through the help documents and their source codes.
type
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
procedure FormClick(Sender: TObject);
procedure IdTCPServer1Execute(AContext: TIdContext);
private
TestContext: TIdContext;
end;
procedure TForm1.FormClick(Sender: TObject);
begin
TestContext.Connection.Disconnect; // access violation
TestContext.Connection.IOHandler.Close; // always works well
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
TestContext := AContext;
AContext.Connection.Disconnect; // works well
end;
TIdTCPConnection.Disconnect() calls IOHandler.Close() internally, if an IOHandler is assigned and has been opened (it also calls TIdTCPConnection.DisconnectNotifyPeer() and triggers the OnDisconnected and OnStatus events):
procedure TIdTCPConnection.Disconnect(ANotifyPeer: Boolean);
var
// under ARC, convert a weak reference to a strong reference before working with it
LIOHandler: TIdIOHandler;
begin
try
// Separately to avoid calling .Connected unless needed
if ANotifyPeer then begin
// TODO: do not call Connected() here if DisconnectNotifyPeer() is not
// overriden. Ideally, Connected() should be called by overridden
// DisconnectNotifyPeer() implementations if they really need it. But
// to avoid any breakages in third-party overrides, we could check here
// if DisconnectNotifyPeer() has been overridden and then call Connected()
// to maintain existing behavior...
//
try
if Connected then begin
DisconnectNotifyPeer;
end;
except
// TODO: maybe allow only EIdConnClosedGracefully and EIdSocketError?
end;
end;
finally
{
there are a few possible situations here:
1) we are still connected, then everything works as before,
status disconnecting, then disconnect, status disconnected
2) we are not connected, and this is just some "rogue" call to
disconnect(), then nothing happens
3) we are not connected, because ClosedGracefully, then
LConnected will be false, but the implicit call to
CheckForDisconnect (inside Connected) will call the events
}
// We dont check connected here - we realy dont care about actual socket state
// Here we just want to close the actual IOHandler. It is very possible for a
// socket to be disconnected but the IOHandler still open. In this case we only
// care of the IOHandler is still open.
//
// This is especially important if the socket has been disconnected with error, at this
// point we just want to ignore it and checking .Connected would trigger this. We
// just want to close. For some reason NS 7.1 (And only 7.1, not 7.0 or Mozilla) cause
// CONNABORTED. So its extra important we just disconnect without checking socket state.
LIOHandler := IOHandler;
if Assigned(LIOHandler) then begin
if LIOHandler.Opened then begin
DoStatus(hsDisconnecting);
LIOHandler.Close;
DoOnDisconnected;
DoStatus(hsDisconnected);
//LIOHandler.InputBuffer.Clear;
end;
end;
end;
end;
TIdIOHandler.Close() simply closes the socket, if one has been allocated:
procedure TIdIOHandlerSocket.Close;
begin
if FBinding <> nil then begin
FBinding.CloseSocket;
end;
inherited Close;
end;
procedure TIdIOHandler.Close;
//do not do FInputBuffer.Clear; here.
//it breaks reading when remote connection does a disconnect
var
// under ARC, convert a weak reference to a strong reference before working with it
LIntercept: TIdConnectionIntercept;
begin
try
LIntercept := Intercept;
if LIntercept <> nil then begin
LIntercept.Disconnect;
end;
finally
FOpened := False;
WriteBufferClear;
end;
end;
The reason for your access violation error is likely because your test code is not thread-safe to begin with. TIdTCPServer is a multi-threaded component. Its OnConnect, OnDisconnect, OnExecute, and OnException events are triggered in the context of a worker thread that manages the TIdContext object. Your OnClick handler is accessing the TIdContext object outside of that thread. As soon as the socket is closed, TIdTCPServer will detect that and stop the thread, destroying the TIdContext and its TIdTCPConnection and TIdIOHandler objects. Due to thread timing and context switching, your OnClick handler may very well be continuing to access those objects after they have been destroyed. You don't have that problem inside of the OnExecute handler because the objects are still valid while the thread is running.
To make your OnClick code play nice with TIdTCPServer, you need to lock the TIdTCPServer.Contexts list so the TIdContext object cannot be destroyed while OnClick is still trying to use it, eg:
procedure TForm1.FormClick(Sender: TObject);
var
List: TIdContextList;
begin
List := IdTCPServer1.Contexts.LockList;
try
//has the context already been removed?
if List.IndexOf(TestContext) <> -1 then
TestContext.Connection.Disconnect;
finally
IdTCPServer1.Contexts.UnlockList;
end;
end;
Related
I'm using Delphi 10 Seattle to build a simple Client/Server application using the TIdTCPClientand TIdTCPServer components.
For read the data arrived from the server application (TIdTCPServer) I'm using a Thread in the Client Application.
This is the Execute method
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
if FClient.Connected() then //FClient is TIdTCPClient
begin
if not FClient.IOHandler.InputBufferIsEmpty then
begin
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
end
else
FClient.IOHandler.CheckForDataOnSource(10);
end;
except
on E: Exception do
begin
// Send the exception message to the logger
FE:=E;
Synchronize(LogException);
end;
end;
end;
end;
Under normal circumstances all is working fine, but now I'm doing some tests to restore the connection on the client application in case which the server or the network is down. So I shutdown the server App to simulate a issue when the comm fails.
When that happens the client application detects which the server is gone using the TIdTCPClient.OnStatus event.
After that I try to terminate the reading thread using this code
if Assigned(FClientReadThr) then
begin
FClientReadThr.Terminate;
FClientReadThr.WaitFor; // This never returns.
FreeAndNil(FClientReadThr);
end;
But the WaitFor function never returns.
SO the question is , there is something wrong on my execute procedure which is preventing the finalization of the thread?
Exist a better way to terminate the thread?
First, you should not be using Connected() in this manner. Just call ReadLn() unconditionally and let it raise an exception if an error/disconnect occurs:
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
except
// ...
end;
end;
end;
If you want to poll the socket for data manually, it should look more like this:
procedure TClientReadThread.Execute;
begin
while not Terminated do
begin
try
if FClient.IOHandler.InputBufferIsEmpty then
begin
FClient.IOHandler.CheckForDataOnSource(10);
FClient.IOHandler.CheckForDisconnect;
if FClient.IOHandler.InputBufferIsEmpty then Continue;
end;
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
except
// ...
end;
end;
end;
DO NOT use the TIdTCPClient.OnStatus event to detect a disconnect in this situation. You are deadlocking your code if you are terminating the thread directly in the OnStatus event handler. That event will be called in the context of the thread, since the thread is the one reading the connection and detecting the disconnect. So your thread ends up waiting on itself, that is why WaitFor() does not exit.
I would suggest an alternative approach. DON'T terminate the thread at all. To recover the connection, add another level of looping to the thread and let it detect the disconnect and reconnect automatically:
procedure TClientReadThread.Execute;
var
I: Integer;
begin
while not Terminated do
begin
try
// don't call Connect() in the main thread anymore, do it here instead
FClient.Connect;
except
// Send the exception message to the logger
// you should wait a few seconds before attempting to reconnect,
// don't flood the network with connection requests...
for I := 1 to 5 do
begin
if Terminated then Exit;
Sleep(1000);
end;
Continue;
end;
try
try
while not Terminated do
begin
AResponse := FClient.IOHandler.ReadLn();
Synchronize(NotifyReadln);
end;
except
// Send the exception message to the logger
end;
finally
FClient.Disconnect;
end;
end;
end;
You can then Terminate() and WaitFor() the thread normally when you want to stop using your socket I/O.
I need to set custom timeout for TTcpClient. I think the default timeout time is about 20-25 seconds but I need to change it to 500ms. Is it possible And How?
procedure TForm1.Button1Click(Sender: TObject);
begin
TcpClient2.RemoteHost := '192.168.1.1';
TcpClient2.RemotePort := '23';
TcpClient2.Connect;
tcpclient2.Receiveln();
tcpclient2.Sendln('admin');
tcpclient2.Receiveln;
end;
I tried non-blocking option but the software returns an error after I click on button And I have to do it again 4-5 times. Any help?
Thanks :)
Winsock has no connect timeout, but this can be overcomed.
You have several options:
Without threads:
Using non-blocking mode: call Connect, then wait using Winsock select function (encapsulated in TBaseSocket Select method inherited by TTcpClient).
Using blocking mode: changing temporarily to non-blocking mode and proceeding as in the previous case.
With threads: see Remy Lebeau's answer to How to control the connect timeout with the Winsock API?.
Use Indy.
Blocking vs non-blocking
Using blocking or non-blocking mode is a very important design decision that will affect many of your code and which you can't easily change afterward.
For example, in non-blocking mode, receive functions (as Receiveln), will not wait until there is enough input available and could return with an empty string. This can be an advantage if is this what you need, but you need to implement some strategy, such as waiting using TcpClient.WaitForData before calling the receive function (in your example, the Receiveln-Sendln-Receiveln will not work as is).
For simple tasks, blocking mode is easier to deal with.
Non-blocking mode
The following function will wait until the connection is successful or the timeout elapses:
function WaitUntilConnected(TcpClient: TTcpClient; Timeout: Integer): Boolean;
var
writeReady, exceptFlag: Boolean;
begin
// Select waits until connected or timeout
TcpClient.Select(nil, #writeReady, #exceptFlag, Timeout);
Result := writeReady and not exceptFlag;
end;
How to use:
// TcpClient.BlockMode must be bmNonBlocking
TcpClient.Connect; // will return immediately
if WaitUntilConnected(TcpClient, 500) then begin // wait up to 500ms
... your code here ...
end;
Also be aware of the following drawbacks/flaws in TTcpClient's non-blocking mode design:
Several functions will call OnError with SocketError set to WSAEWOULDBLOCK (10035).
Connected property will be false because is assigned in Connect.
Blocking mode
Connection timeout can be achieved by changing to non-blocking mode after socket is created but before calling Connect, and reverting back to blocking mode after calling it.
This is a bit more complicated because TTcpClient closes the connection and the socket if we change BlockMode, and also there is not direct way of creating the socket separately from connecting it.
To solve this, we need to hook after socket creation but before connection. This can be done using either the DoCreateHandle protected method or the OnCreateHandle event.
The best way is to derive a class from TTcpClient and use DoCreateHandle, but if for any reason you need to use TTcpClient directly without the derived class, the code can be easily rewriten using OnCreateHandle.
type
TExtendedTcpClient = class(TTcpClient)
private
FIsConnected: boolean;
FNonBlockingModeRequested, FNonBlockingModeSuccess: boolean;
protected
procedure Open; override;
procedure Close; override;
procedure DoCreateHandle; override;
function SetBlockModeWithoutClosing(Block: Boolean): Boolean;
function WaitUntilConnected(Timeout: Integer): Boolean;
public
function ConnectWithTimeout(Timeout: Integer): Boolean;
property IsConnected: boolean read FIsConnected;
end;
procedure TExtendedTcpClient.Open;
begin
try
inherited;
finally
FNonBlockingModeRequested := false;
end;
end;
procedure TExtendedTcpClient.DoCreateHandle;
begin
inherited;
// DoCreateHandle is called after WinSock.socket and before WinSock.connect
if FNonBlockingModeRequested then
FNonBlockingModeSuccess := SetBlockModeWithoutClosing(false);
end;
procedure TExtendedTcpClient.Close;
begin
FIsConnected := false;
inherited;
end;
function TExtendedTcpClient.SetBlockModeWithoutClosing(Block: Boolean): Boolean;
var
nonBlock: Integer;
begin
// TTcpClient.SetBlockMode closes the connection and the socket
nonBlock := Ord(not Block);
Result := ErrorCheck(ioctlsocket(Handle, FIONBIO, nonBlock)) <> SOCKET_ERROR;
end;
function TExtendedTcpClient.WaitUntilConnected(Timeout: Integer): Boolean;
var
writeReady, exceptFlag: Boolean;
begin
// Select waits until connected or timeout
Select(nil, #writeReady, #exceptFlag, Timeout);
Result := writeReady and not exceptFlag;
end;
function TExtendedTcpClient.ConnectWithTimeout(Timeout: Integer): Boolean;
begin
if Connected or FIsConnected then
Result := true
else begin
if BlockMode = bmNonBlocking then begin
if Connect then // will return immediately, tipically with false
Result := true
else
Result := WaitUntilConnected(Timeout);
end
else begin // blocking mode
// switch to non-blocking before trying to do the real connection
FNonBlockingModeRequested := true;
FNonBlockingModeSuccess := false;
try
if Connect then // will return immediately, tipically with false
Result := true
else begin
if not FNonBlockingModeSuccess then
Result := false
else
Result := WaitUntilConnected(Timeout);
end;
finally
if FNonBlockingModeSuccess then begin
// revert back to blocking
if not SetBlockModeWithoutClosing(true) then begin
// undesirable state => abort connection
Close;
Result := false;
end;
end;
end;
end;
end;
FIsConnected := Result;
end;
How to use:
TcpClient := TExtendedTcpClient.Create(nil);
try
TcpClient.BlockMode := bmBlocking; // can also be bmNonBlocking
TcpClient.RemoteHost := 'www.google.com';
TcpClient.RemotePort := '80';
if TcpClient.ConnectWithTimeout(500) then begin // wait up to 500ms
... your code here ...
end;
finally
TcpClient.Free;
end;
As noted before, Connected doesn't work well with non-blocking sockets, so I added a new IsConnected property to overcome this (only works when connecting with ConnectWithTimeout).
Both ConnectWithTimeout and IsConnected will work with both blocking and non-blocking sockets.
I have a TidTCPServer which use database manipulating inside onExcecute event (by using TidNotify). Everything works very good instead of possibility closing application.
During closing application I do not know whether everything Notify instances finished their work or not and usually I get Runtime Error 216 (I think I close database before "notify" work end).
Is any way to check - are there waiting old Notify posts or not to be sure I can close application.
Other question is how to protect TidTCPServer from accepting new connection during closing server process.
I use code like below but I obtain the error still.
type
TShutdownThread = class(TThread)
protected
procedure Execute; override;
end;
procedure TShutdownThread.Execute;
begin
IdTCPServer.Active := false;
end;
//closing...
if IdTCPServer.Active then
begin
with TShutdownThread.Create(false) do
try
WaitFor; // internally processes sync requests...
finally
Free;
end;
end;
Is any way to check - are there
waiting old Notify posts or not to be sure I can close
application.
TIdNotify is asynchronous, it posts requests to the main thread message queue for later execution. It is possible that pending requests are still in the queue after TShutdownThread.WaitFor() has exited. You can call the RTL's CheckSynchronize() function to process any remaining requests, eg:
if IdTCPServer.Active then
begin
with TShutdownThread.Create(false) do
try
WaitFor;
finally
Free;
end;
CheckSynchronize;
end;
how to protect TidTCPServer from accepting new connection during closing server process.
While TIdTCPServer is being deactivated, it closes its listening port(s) for you. However, there is a very small window of opportunity when new clients could be accepted before the server closes the port(s). The server will close those connections as part of its shutdown, but if you do not want the OnExecute event to be called for those connections then you can set a flag somewhere in your code before deactivating the server, then check for that flag in the OnConnect event, and if it is set then disconnect the client immediately, eg:
var
ShuttingDown: boolean = False;
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
if ShuttingDown then
begin
AContext.Connection.Disconnect;
Exit;
end;
...
end;
...
if IdTCPServer.Active then
begin
ShuttingDown := True;
try
with TShutdownThread.Create(false) do
try
WaitFor;
finally
Free;
end;
CheckSynchronize;
finally
ShuttingDown := False;
end;
end;
to write information on the processing state to the GUI inside a tcpserver.onexecute(..) function , i used the following command sequence
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess;
TThread.Synchronize(nil, BitMap_PaintImageProcess);
The code is working well on some machines, but on a few it fails. The code execution stops atTThread.Synchronize command. I guess on these machines the function call is trapped inside a deadlock
Any chance to figure out the real problem behind ?
The procedure BitMap_PaintImageProcess , here I create a Bitmap and do a lot of painting stuff , but is seems that this code is never executed ?
I try to explain the very long code and reduce to the main points , the critical thread issues are hidden in processing the bitmap inside my Bitmapprocessingclass.
This class is accessed inside the GUIProcessing procedures of my ServerMainForm which also has the INDY TCP Server component.
{--------------- CLASS DEFINITION -----------------------------}
TBitMapProcessingClass = class()
FBitmap : TBitmap;
FList : TListOfSomething;
procedure ProcessTheBitmap(....);
......
(many many functions);
procedure Init;
procedure Free;
Procedure Create;
end;
TMainform = class(TForm)
MyServer : TIdTCPServer;
aBitMaoProcessingClass : TBitMaoProcessingClass;
procedure BitMap_PaintImageProcess;
procedure BitMap_ListProcess;
.....
end;
{------------------------- Implemantation ------------------------------}
procedure TMainform.IndyTCPServer.Onexecute()
begin
.......
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess;
TThread.Synchronize(nil, BitMap_PaintImageProcess);
.......
end;
procedure TMainform.BitMap_PaintImageProcess;
begin
DoSomeServerVCLStuff(....);
aBitMapProcessingClass.ProcessTheBitmap;
DoSomeServerVCLStuff(....);
end;
Having no idea what BitMap_PaintImageProcess() does in fact, I have a few suppositions:
In the TThread.Synchronize call you try to read some data from the socket/idContext, but the data is not yet available. This will block the main thread until the data becomes available. But Indy's thread that is responsible for reading from the underlying socket buffer is currently blocked by your TThread.Synchronize call in the OnExecute event i.e. deadlock occurs;
In the TThread.Synchronize call you use Application.ProcessMessages (a common mistake);
In the OnExecute event you enter a critical section. Then during the TThread.Synchronize call you try to enter the same critical section again;
You modify the GUI in the onExecute event. Because onExecute is not thread safe, accessing VCL/FM from Indy's thread could lead to unpredictable results, including random deadlocks (hard to find).
I would suggest to use MadExcept / Eurekalog. They both have options to check if the main thread is "frozen". When that happens (during the deadlock) they will show you the current call stack. Having the call stack you can figure out which function is causing the deadlock.
Regarding the posted code:
procedure TMainform.IndyTCPServer.Onexecute()
begin
.......
ExecuteDUMMYCommand(Global_Send_Record);
BitMap_PaintImageProcess; //-> You do VCL stuff in the context of Indy's thread!
TThread.Synchronize(nil, BitMap_PaintImageProcess);
end;
In the BitMap_PaintImageProcess() you call DoSomeServerVCLStuff(....). Do not forget that OnExecute is fired from Indy's thread for the current context. I.e. you modify VCL from another thread (other from the Main Thread) which is not thread safe.
On your comment:
...but here my complex TBitmap processing Class must be alive the whole
time my Server is active...
If you have only one (global) instance for image processing, then what will happen if another client connects, while you are still processing the old connection (think Parallel :) )? Your image processing class should be instantiated separately for each new connection/context. For GUI updating you can use TIdNotify descendant.
A possible solution:
type
{ tIdNotify Stuff }
TVclProc= procedure(imgClass: tMyImageProcessingClass) of object;
tIdNotifyDescendant = (tIdNotify)
protected
fImgClass: tMyImageProcessingClass;
fProc: TVclProc;
procedure DoNotify; override;
public
class procedure updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
end;
procedure tIdNotifyDescendant.DoNotify;
begin
inherited DoNotify;
FProc(fImgClass);
end;
class procedure tIdNotifyDescendant.updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
begin
with Create do
begin
fImgClass := imgClass;
fProc := vclProc;
Notify;
end;
end;
{ Indy stuff & other logic }
procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
// Create your instance when the client connects
AContext.Data := tMyImageProcessingClass.Create;
end;
procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
// Do cleanup
if assinged(AContext.Data) then
(AContext.Data as tMyImageProcessingClass).Free // Casting just for clarity
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
imgProcClass: tMyImageProcessingClass;
begin
imgProcClass := acontext.Data as tMyImageProcessingClass;
// Do image processing
// Notify GUI for the progress:
tIdNotifyDescendant.updateVcl(AContext.data as tMyImageProcessingClass);
end;
Tip: If you do JPEG processing and you use Draw() method have in mind this: TJPEGImage.Draw() is not thread safe
I added a few more details on my BitmapProcessingclass and the idea of a thread safe extension of the existing class...
I need the existing class u nchanged in others apps ... I need a extension inside my app with the indy server.
ONly one client my connect to one server, or he has to query the state of the server
type TBmpNotify = class(TIdNotify)
protected
FBMP: MyImageProcessingClass;
procedure DoNotify; override;
public
constructor Create(aBMP: MyImageProcessingClass);
function SetImageView(LL, UR: TPoint): Boolean;
procedure PaintBitMap;
function InitBitMap(x, y: Integer;
PixelFormat: TPixelFormat = pf24bit): Boolean;
destructor free;
end;
implementation
{ TBmpNotify }
constructor TBmpNotify.Create(aBMP: MyImageProcessingClass);
begin
// indise this class I also create
// class.TBitmap
// class.TList
// much more stuff ....
FBmp := MyImageProcessingClass.Create;
end;
procedure TBmpNotify.DoNotify;
begin
inherited;
end;
destructor TBmpNotify.free;
begin
FBmp.Free;
inherited;
end;
function TBmpNotify.InitBitMap(x, y: Integer;
PixelFormat: TPixelFormat): Boolean;
begin
// Write values to the List
// also modify TBitmap
// execution time of this function ~ 5 min
FBmp.InitBitMap(x,y,PixelFormat)
end;
procedure TBmpNotify.PaintBitMap;
begin
// much TBitmap, bitmap.canvas .... is used
// execution time of this function ~ 1 min
FBmp.PaintBitMap;
end;
function TBmpNotify.SetImageView(LL, UR: TPoint): Boolean;
begin
// this function takes about 1 min
FBmp.SetImageView(LL, UR);
end;
end.
For some specific needs i need to create procedure that waits for socket request (or answer) in dll:
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
......
procedure MyWaitProc; stdcall;
begin
Go := false;
while not Go do
begin
// Wating...
// Application.ProcessMessages; // Works with this line
end;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
MessageBoxA(0, PAnsiChar('Received: '+Socket.ReceiveText), '', MB_OK);
Go := true;
end;
exports
MyWaitProc;
When I call Application.ProcessMessages everything works fine: application waits for request and then continues. But in my case calling Application.ProcessMessages causes to unlocking main form on host application (not dll's one). When I don't call Application.ProcessMessages application just hangs couse it cannot handle message...
So, how to create such a procedure that's wating for socket answer ?
Maybe there a way to wait for socket answer without using Application.ProcessMessages ?
EDIT
I also tried to use TIdTCPServer, for some reasons, the result is the same.
TForm1 = class(TForm)
IdTCPServer1: TIdTCPServer;
.....
procedure MyWaitProc; stdcall;
begin
Go := false;
while not Go do
begin
// Waiting ...
// Application.ProcessMessages;
end;
end;
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
s: string;
begin
s := AContext.Connection.Socket.ReadString(1);
AllText := AllText + s;
Go := True;
end;
TServerSocket runs in non-blocking mode by default, which depends on processing window messages. To remove that dependancy, you have to switch it to blocking mode instead.
TIdTCPServer runs in blocking mode exclusively, so no window messages. If you are having a problem with it, then you are misusing it. For example, in your TServerSocket code, you do not set Go = True until after a response has been received, but in your TServerSocket code you are setting Go = True before reading a response instead.
As an alternative, have a look at Indy's TIdSimpleServer component. TIdSimpleServer is synchronous and only accepts 1 connection at a time, whereas TIdTCPServer is asynchronous and accepts many connections at a time. For example:
TForm1 = class(TForm)
ServerSocket: TIdSimpleServer;
procedure MyWaitProc; stdcall;
var
s: String;
begin
ServerSocket.Listen;
s := ServerSocket.IOHandler.ReadLn;
ServerSocket.Disconnect;
MessageBox(0, PChar('Received: '+s), '', MB_OK);
end;
exports
MyWaitProc;
Rather than creating a loop that occasionally calls Application.ProcessMessages you can create a descendant of TThread and move the socket request to the TThread.Execute method. Use TThread.OnTerminate to notify your form(or any other class) when the thread has completed its work.
There is sample code which gives more details about how to use TThread.
There are several other 3rd party threading libraries that either provide more flexibility or are easier to use than TThread and I would highly recommend any of them over TThread if you are new to multi-threading.
Note: There are some serious side-effects to using Application.ProcessMessages. You are seeing one of them in your code with the dll unlocking the application's mainform. It breaks the single-threaded UI model the VCL is build upon. ProcessMessages has its place but using threads is more appropriate for the situation you're describing.
var Slowpoke: TMyLongRunningProcessThread;
procedure MyWaitProc(Completed:TNotifyEvent)
begin
Slowpoke := TMyLongRunningProcessThread.Create(True);
Slowpoke.FreeOnTerminate := True;
Slowpoke.OnTerminate := Completed;
Slowpoke.Resume;
end;
MyWaitProc returns immediately after starting the thread so the GUI is free to respond to user actions. When the thread terminates it calls the event handler pointed to by Completed.
Obviously if you need to retrieve data from the thread you'll want to either have the thread write to an accessible memory location before it Frees itself or remove the FreeOnTerminate so the data can be retreived from the thread through a property.