Search code examples
linuxperformancetcplazarusindy10

Indy 10 sends data in 1024 chunks. How to increase chunk size?


I have developed two softwares side by side.

One is a TCP client for iOS devices, which is developed in XCode, the other one is a TCP server for Linux OS, which is developed in Lazarus using Indy 10 as the networking framework.

I can send data between the server and the client with no major problems, however the speed of data transfer from the server to the client is an issue to me.

When sending data from the iOS client to the Linux server I get very good transfer speeds of up to 20 MB a second, but when sending data from the Linux server to the iOS client I get only up to a hundred kilobytes a second (100kb).

Indy 10 sends data in 1024 byte chunks, and I can see that in my iOS app by using NSLog().

Question: How can I change the behaviour of Indy 10 to send larger chunks at once?

EDIT: I am using the following code to send a TMemoryStream:

procedure TMyClass.SendData(aData: TMemoryStream);
  var i: integer;
      ctx : TIdContext;
begin
  aData.Position := 0;
  with fIdTCP.Contexts.LockList do
  begin
    for i := 0 to Count -1 do //I'm broadcasting the data
    begin
      ctx := TIdContext(Items[i]);
      ctx.Connection.IOHandler.LargeStream:=true;
      ctx.Connection.IOHandler.Write(aData, aData.Size, false);
    end;

    ctx.Connection.IOHandler.WriteBufferClose;
    fIdTCP.Contexts.UnlockList;
  end   
end;

Solution

  • Indy is not the one limiting the size of the sent packets. Its default buffer size is 32K (see the TIdIOHandler.SendBufferSize property) when reading from the TMemoryStream (which is limited only by available memory on reads) and then passes however many bytes were read to the underlying socket for sending.

    Linux might be the one limiting the size of the sends. The default send buffer size of the underlying socket can be 1024 bytes. The Linux documentation states the following:

    socket - Linux socket interface

    Socket options

    The socket options listed below can be set by using setsockopt(2) and read with getsockopt(2) with the socket level set to SOL_SOCKET for all sockets. Unless otherwise noted, optval is a pointer to an int.

    ...

    SO_SNDBUF
    Sets or gets the maximum socket send buffer in bytes. The kernel doubles this value (to allow space for bookkeeping overhead) when it is set using setsockopt(2), and this doubled value is returned by getsockopt(2). The default value is set by the /proc/sys/net/core/wmem_default file and the maximum allowed value is set by the /proc/sys/net/core/wmem_max file. The minimum (doubled) value for this option is 2048.

    So check if your Linux's wmem_default/wmem_max configuration is limiting the transmitted packets to 1024 bytes or not.

    You can use Indy's TIdSocketHandle.SetSockOpt() method to try to specify a different buffer size (within Linux's configured limits), eg:

    uses
      ..., IdStackConsts;
    
    procedure TMyForm.MyTCPServerConnect(AContext: TIdContext);
    var
      BufferSize: Integer;
    begin
      BufferSize := ...;
      AContext.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDBUF, BufferSize);
      AContext.Binding.GetSockOpt(Id_SOL_SOCKET, Id_SO_SNDBUF, BufferSize);
      // BufferSize now contains the ACTUAL buffer size used by the socket
      // which may be different than what you requested...
    end;
    

    If the buffer size is not being limited by Linux, then it is likely being limited by iOS when receiving the data. Make sure your iOS app is not limiting its input buffer to 1024 bytes when reading from an NSInputStream in your stream event handler.