I have a problem with Indy TCP connection. I use Turbo Delphi 2006 with Indy 10. I want to send multiple TCP packages from idTCPClient to idTCPServer.
It works finely, when I want to send only one package, or I insert a sleep(100) command between two calls of the function. But if I call this function too frequently, it doesn't call the server's onExecute every time.
My code for sending:
procedure SendData(var data: TIdBytes) ;
begin
FormMain.IdTCPClient.Connect ;
FormMain.IdTCPClient.Socket.Write(data);
FormMain.IdTCPClient.Disconnect ;
end ;
I call this function several times (5-10 times in a second), and want to process all of these packages in my server application:
procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
data: TIdBytes ;
begin
AContext.Connection.IOHandler.ReadBytes(data, 4, false)
// processing data
end
Thank you for your answers in advance!
Every time you call Connect()
, you are creating a new connection, and TIdTCPServer
will start a new thread to handle that connection (unless you enable thread pooling, that is). Is that what you really want? It would be more efficient to have the client leave the connection open for a period of time and reuse the existing connection as much as possible. Disconnect the connection only when you really do not need it anymore, such as when it has been idle for awhile. Establishing a new connection is an expensive operation on both ends, so you should reduce that overhead as much as possible.
On the client side, when you call Write(data)
, it will send the entire TIdBytes
, but you are not sending the length of that TIdBytes
to the server so it knowns how many bytes to expect. TIdIOHandler.Write(TIdBytes)
does not do that for you, you have to do it manually.
On the server side, you are telling ReadBytes()
to read only 4 bytes at a time. After each block of 4 bytes, you are exiting the OnExecute
event handler and waiting for it to be called again to read the next block of 4 bytes. Unless the length of the client's source TIdBytes
is an even multiple of 4, ReadBytes()
will raise an exception (causing the server to disconnect the connection) when it tries to read the client's last block that is less than 4 bytes, so your server code will not receive that block.
Try this instead:
procedure SendData(var data: TIdBytes) ;
begin
FormMain.IdTCPClient.Connect;
try
FormMain.IdTCPClient.IOHandler.Write(Longint(Length(data)));
FormMain.IdTCPClient.IOHandler.Write(data);
finally
FormMain.IdTCPClient.Disconnect;
end;
end;
procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
data: TIdBytes;
begin
with AContext.Connection.IOHandler do
ReadBytes(data, ReadLongint, false);
// process data
end;
With that said, if changing the client code to send the TIdBytes
length is not an option for whatever reason, then use this server code instead:
procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
LBytes: Integer;
data: TIdBytes;
begin
// read until disconnected. returns -1 on timeout, 0 on disconnect
repeat until AContext.Connection.IOHandler.ReadFromSource(False, 250, False) = 0;
AContext.Connection.IOHandler.InputBuffer.ExtractToBytes(data);
// process data
end;
Or:
procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
strm: TMemoryStream;
data: TIdBytes;
begin
strm := TMemoryStream.Create;
try
// read until disconnected
AContext.Connection.IOHandler.ReadStream(strm, -1, True);
strm.Position := 0;
ReadTIdBytesFromStream(strm, data, strm.Size);
finally
strm.Free;
end;
// process data
end;
Or:
procedure TFormMain.IdTCPServerMainExecute(AContext: TIdContext);
var
strm: TMemoryStream;
begin
strm := TMemoryStream.Create;
try
// read until disconnected
AContext.Connection.IOHandler.ReadStream(strm, -1, True);
// process strm.Memory up to strm.Size bytes
finally
strm.Free;
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With