我假设 100 字节太小,并且可能会减慢所有写入的较大文件传输速度,但是像 1MB 这样的大小似乎太多了。有没有人对通过网络发送数据的每次写入的最佳字节块有任何建议?
为了详细说明,我正在实现通过网络连接发送数据并显示发送数据的进度的东西。我注意到如果我以每次写入大约 100 字节的速度发送大文件,它会非常慢,但进度条效果很好。但是,如果我以每次写入 1M 的速度发送,它会快得多,但由于发送的块较大,进度条的工作效果不佳。
我假设 100 字节太小,并且可能会减慢所有写入的较大文件传输速度,但是像 1MB 这样的大小似乎太多了。有没有人对通过网络发送数据的每次写入的最佳字节块有任何建议?
为了详细说明,我正在实现通过网络连接发送数据并显示发送数据的进度的东西。我注意到如果我以每次写入大约 100 字节的速度发送大文件,它会非常慢,但进度条效果很好。但是,如果我以每次写入 1M 的速度发送,它会快得多,但由于发送的块较大,进度条的工作效果不佳。
不,没有通用的最佳字节大小。
TCP 数据包容易产生碎片,虽然假设从这里到目的地的所有东西都是真正的以太网,数据包大小很大,但现实情况是,即使您可以从一个数据包中获取所有单个网络的数据包大小需要,您发送的每个数据包都可能通过互联网采用不同的路径。
这不是您可以“解决”的问题,也没有通用的理想尺寸。
尽可能快地将数据提供给操作系统和 TCP/IP 堆栈,它会动态调整数据包大小以适应网络连接(您应该看到他们用于此优化的代码 - 这真的非常有趣。至少在更好的堆栈上。)
但是,如果您控制所有正在使用的网络和堆栈以及客户端/服务器之间的所有网络和堆栈,那么您可以进行一些手动调整。但通常即使那样,在我建议您接近它之前,您也必须非常了解网络和您发送的数据。
-亚当
如果可以的话,就让IP栈来处理吧;大多数操作系统已经内置了很多优化。例如,Vista 将动态改变各种参数以最大化吞吐量;事后猜测算法不太可能是有益的。
在远离实际线路的高阶语言中尤其如此,例如 C#;你和实际的 TCP/IP 数据包之间有足够的层,我希望你的代码对吞吐量的影响相对较小。
在最坏的情况下,为自己测试各种情况下的各种消息大小;很少有解决方案是一刀切的。
如果您在以太网上使用 TCP/IP,则最大数据包大小约为 1500 字节。如果您尝试一次发送多个数据包,则数据将被拆分为多个数据包,然后再通过网络发送出去。如果您的应用程序中的数据已经打包,那么您可能希望选择小于 1500 的数据包大小,以便在发送完整数据包时,底层堆栈不必将其分解。例如,如果您执行的每次发送都是 1600 字节,则 TCP 堆栈将必须为每次发送发送两个数据包,第二个数据包大部分为空。这是相当低效的。
话虽如此,我不知道这会对性能产生多大的明显影响。
制作一个名为 CalcChunkSize 的函数,将一些私有变量添加到您的类中:
Private PreferredTransferDuration As Integer = 1800 ' milliseconds, the timespan the class will attempt to achieve for each chunk, to give responsive feedback on the progress bar.
Private ChunkSizeSampleInterval As Integer = 15 ' interval to update the chunk size, used in conjunction with AutoSetChunkSize.
Private ChunkSize As Integer = 16 * 1024 ' 16k by default
Private StartTime As DateTime
Private MaxRequestLength As Long = 4096 ' default, this is updated so that the transfer class knows how much the server will accept
在每次下载块之前,检查是否需要使用 ChunkSizeSampleInterval 计算新的块大小
Dim currentIntervalMod As Integer = numIterations Mod Me.ChunkSizeSampleInterval
If currentIntervalMod = 0 Then
Me.StartTime = DateTime.Now
ElseIf currentIntervalMod = 1 Then
Me.CalcChunkSize()
End If
numIterations 在下载循环之外设置为 0,并且在每个下载的块设置为 numIterations += 1 之后
让 CalcChunkSize 这样做:
Protected Sub CalcAndSetChunkSize()
' chunk size calculation is defined as follows
' * in the examples below, the preferred transfer time is 1500ms, taking one sample.
' *
' * Example 1 Example 2
' * Initial size = 16384 bytes (16k) 16384
' * Transfer time for 1 chunk = 800ms 2000 ms
' * Average throughput / ms = 16384b / 800ms = 20.48 b/ms 16384 / 2000 = 8.192 b/ms
' * How many bytes in 1500ms? = 20.48 * 1500 = 30720 bytes 8.192 * 1500 = 12228 bytes
' * New chunksize = 30720 bytes (speed up) 12228 bytes (slow down from original chunk size)
'
Dim transferTime As Double = DateTime.Now.Subtract(Me.StartTime).TotalMilliseconds
Dim averageBytesPerMilliSec As Double = Me.ChunkSize / transferTime
Dim preferredChunkSize As Double = averageBytesPerMilliSec * Me.PreferredTransferDuration
Me.ChunkSize = CInt(Math.Min(Me.MaxRequestLength, Math.Max(4 * 1024, preferredChunkSize)))
' set the chunk size so that it takes 1500ms per chunk (estimate), not less than 4Kb and not greater than 4mb // (note 4096Kb sometimes causes problems, probably due to the IIS max request size limit, choosing a slightly smaller max size of 4 million bytes seems to work nicely)
End Sub
然后在请求下一个块时只需使用 ChunkSize。
我在 Tim_mackey 的“使用 MTOM Web 服务和 .Net 2.0 以块发送文件”中发现了这一点,并且发现动态计算最有效的块大小非常有用。
整个源代码在这里: http: //www.codeproject.com/KB/XML/MTOMWebServices.aspx
作者在这里:http ://www.codeproject.com/script/Membership/Profiles.aspx?mid=321767
我相信你的问题是你使用阻塞套接字而不是非阻塞套接字。
当您使用阻塞套接字并发送 1M 数据时,网络堆栈可以等待所有数据放入缓冲区中,如果缓冲区已满,您将被阻塞,您的进度条将等待整个 1M接受到缓冲区,这可能需要一段时间,你的进度条会跳动。
但是,如果您使用非阻塞套接字,则无论您使用什么缓冲区大小都不会阻塞,并且您需要使用 select/poll/epoll/whatever-works-on-your-platform 来等待自己(尽管 select 是最便携的)。这样,您的进度条将快速更新并反映最准确的信息。
请注意,在发送方,进度条以任何方式被部分破坏,因为内核将缓冲一些数据,并且您将在对方真正收到数据之前达到 100%。解决此问题的唯一方法是,如果您的协议包含对接收方接收到的数据量的回复。
正如其他人所说,第二次猜测操作系统和网络大多是徒劳的,如果你继续使用阻塞套接字选择一个足够大的大小来包含比单个数据包更多的数据,这样你就不会在数据包中发送太少的数据作为这将不必要地降低您的吞吐量。我会使用类似 4K 的东西,一次至少包含两个数据包。
我要补充的一件事是,对于给定的以太网连接,发送一个小数据包和发送一个大数据包所花费的时间差不多。正如其他人所说:如果您只是发送数据流,请让系统处理它。但是,如果您担心来回发送单个短消息,则典型的以太网数据包大约为 1500 字节-只要您将其保持在以下范围内就可以了。
您需要使用Path MTU Discovery,或使用良好的默认值(即小于 1500 字节)。
如果您还没有,您可以进行的一项经验测试当然是使用嗅探器(tcpdump、Wireshark 等)并查看使用其他软件进行上传/下载时达到的数据包大小。这可能会给你一个提示。
这是您需要的公式:
int optimalChunkSize = totalDataSize / progressBar1.Width;
使用它,您发送的每个块都会将进度条增加 1 个像素。就用户反馈而言,比这更小的块大小是没有意义的。