【问题标题】:Indy 10 sends data in 1024 chunks. How to increase chunk size?Indy 10 以 1024 个块发送数据。如何增加块大小?
【发布时间】:2016-01-05 02:02:18
【问题描述】:

我同时开发了两个软件。

一个是iOS设备的TCP客户端,是用XCode开发的, 另一个是 Linux OS 的 TCP 服务器,它是在 Lazarus 中使用 Indy 10 作为网络框架开发的。

我可以在服务器和客户端之间发送数据而没有大问题, 但是从服务器到客户端的数据传输速度对我来说是个问题。

当从 iOS 客户端向 Linux 服务器发送数据时,我获得了高达每秒 20 MB 的非常好的传输速度, 但是当从 Linux 服务器向 iOS 客户端发送数据时,我每秒最多只能获得 100 KB (100kb)。

Indy 10 以 1024 字节块发送数据,我可以在我的 iOS 应用程序中使用 NSLog() 看到这一点。

问题:如何更改 Indy 10 的行为以一次发送更大的块?

编辑: 我正在使用以下代码发送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;

【问题讨论】:

  • 没有足够的信息来回答您的问题。您如何将数据从 Indy 发送到 iOS?你在做什么方法调用?您使用的是 SSL/TLS 吗?这样的事情会有所作为。 Indy 不使用 1KB 缓冲区,除非您告诉它。如果有的话,它的默认缓冲区大小是 32K。因此,Indy 之外的某些东西可能会限制数据大小,具体取决于您的数据来自哪里以及您发送数据的方式。
  • 我更新了问题并添加了代码。没错,缓冲区大小是 32K,我可以测量并更改它,但 iOS 应用程序仍会收到 1024 kb 块。
  • 首先,您不应该在没有之前的WriteBufferOpen() 的情况下调用WriteBufferClose()(并且在发送TStream 数据时不应该使用写缓冲)。其次,UnlockList() 需要在try/finally 块中,因为Write() 可以引发异常。如果客户端断开连接/失败,如果您不解锁列表,您将导致死锁。第三,如果一个客户端出现故障,您不会处理它,因此您最终会跳过广播中的所有后续客户端。仅供参考,这不是使用 Indy 处理 TCP 广播的最佳方式。
  • 在任何情况下,TIdIOHandler.Write(TStream) 分配一个(默认)32K 缓冲区并从TStream 读取该大小的块,将实际上读取的许多字节发送到底层套接字。 TMemoryStream 没有 1024K 的读取限制,Indy 也没有。但是底层套接字可能会对发送施加 1024K 的限制,具体取决于其内部缓冲区大小。您可以使用AContext.Binding.SetSocketOpt(Id_SOL_SOCKET, Id_SO_SNDBUF, ...) 设置该缓冲区大小。或者尝试设置AContext.Connection.Socket.UseNagle := False
  • 感谢您的意见,我会按照您的建议去做。那么SetSocketOpt究竟是如何工作的,我应该写什么来改变内部缓冲区大小?

标签: linux performance tcp lazarus indy10


【解决方案1】:

Indy 不是限制发送数据包大小的那个。从TMemoryStream(仅受读取时的可用内存限制)读取时,其默认缓冲区大小为 32K(请参阅TIdIOHandler.SendBufferSize 属性),然后将读取的许多字节传递到底层套接字进行发送。

Linux 可能会限制发送的大小。底层套接字的默认发送缓冲区大小可以是 1024 字节。 Linux 文档声明如下:

socket - Linux socket interface

套接字选项

可以使用setsockopt(2) 设置下面列出的套接字选项,并使用getsockopt(2) 读取所有套接字的套接字级别设置为SOL_SOCKET。除非另有说明,否则optval 是指向int 的指针。

...

SO_SNDBUF
设置或获取最大套接字发送缓冲区(以字节为单位)。当使用setsockopt(2) 设置它时,内核将这个值加倍(以留出空间用于记账开销),并且这个加倍的值由getsockopt(2) 返回。默认值由/proc/sys/net/core/wmem_default 文件设置,最大允许值由/proc/sys/net/core/wmem_max 文件设置。 此选项的最小(加倍)值为 2048

因此请检查您的 Linux 的 wmem_default/wmem_max 配置是否将传输的数据包限制为 1024 字节。

您可以使用 Indy 的 TIdSocketHandle.SetSockOpt() 方法尝试指定不同的缓冲区大小(在 Linux 配置的限制内),例如:

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;

如果缓冲区大小不受Linux的限制,那么它在接收数据时很可能受到iOS的限制。确保您的 iOS 应用在从 stream 事件处理程序中的 NSInputStream 读取时,其输入缓冲区未限制为 1024 字节。

【讨论】:

  • 这两个 Linux 文件都说 212992。我已将 UseNagle 设置为 false,并将 SetSockOpt 设置为 2048、8000 甚至 212992,但仍然得到 1024 字节的块。
  • 正如我所解释的,Indy 一次从TStream 读取 32K(默认情况下),一次将 32K(或流实际返回的任何内容)传递到底层套接字。在那之后,它就脱离了 Indy 的控制。如果随后的传输小于该值,则限制必须在 Linux 本身或您的网络中。 Linux、您的 WiFi 路由器或您的蜂窝提供商正在将数据包分解为更小的传输。 TCP 允许这样做。使用数据包嗅探器(如 Wireshark)监视您的网络,以了解发生了什么。
  • 我复制了你的代码,仍然是 1024 字节的块。当我将 LNet 用于 Lazarus 时,我从来没有遇到过这个问题,只有 Indy 10,所以说实话,我不认为我的 Linux 配置有任何问题。
  • 但是,我可以向 indy 发送非常大的数据,并且 indy 可以毫无问题地接收各种大小的数据。
  • Indy 使用 32K(默认)缓冲区进行接收。它接收底层套接字决定提供的任何内容。套接字有一个SO_RCVBUF 缓冲区用于接收。该缓冲区从网络接收数据并将副本复制到 Indy 的读取缓冲区中(然后缓存在 Indy 的 InputBuffer 中,然后您的应用会从中读取)。
猜你喜欢
  • 2020-12-16
  • 2019-09-30
  • 2020-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多