.net 对于套接字编程来说,什么是合适的缓冲区大小?

ocebsuys  于 2023-04-22  发布在  .NET
关注(0)|答案(5)|浏览(342)

我们使用.Net和套接字。服务器使用Socket.Sender(bytes[])方法,因此它只发送整个有效负载。另一方面,我们是客户端消费数据。Socket.Receive(buffer[])。在Microsoft的所有示例中(和其他人)他们似乎坚持使用8192的缓冲区大小。我们已经使用了这个大小,但是我们不时地向客户端发送超过这个缓冲区大小的数据。
有没有办法确定服务器的send方法发送给我们多少数据?最佳缓冲区大小是多少?

bpzcxfmw

bpzcxfmw1#

即使您发送的数据比这更多,也可能无法在一次调用Receive时获得。
您无法确定服务器发送了多少数据-它是一个 * 数据流 *,并且您每次只是阅读块。您可能会在一个发送调用中读取服务器发送的 * 部分 * 数据,或者您可能会在一个接收调用中读取两个发送调用的数据。8 K是一个合理的缓冲区大小-不会太大,以至于浪费大量内存,也不要小到你不得不使用大量浪费的接收呼叫。4K或16 K也很可能很好……我个人不会开始使用16 K以上的网络缓冲区--我怀疑你很少会填满它们。
你可以尝试使用一个非常大的缓冲区,并记录每次调用中接收到的字节数--这会给予你对通常有多少可用字节有一些了解--但这并不能真正显示出使用较小缓冲区的效果。你对使用8 K缓冲区有什么顾虑?如果是性能,你有任何证据表明你的代码的这方面是性能瓶颈吗?

cgvd09ve

cgvd09ve2#

不幸的是,Jon Skeet的答案遗漏了很大一部分内容--发送和接收缓冲区的缓冲区大小,以及要写入的管道的bandwidth-delay product
如果你试图通过一个大的管道使用一个套接字发送数据,并且你想让TCP填充那个管道,你需要使用一个发送缓冲区大小和接收缓冲区大小,每个都等于管道的带宽延迟乘积。否则,TCP将不会填充管道,因为它不会在任何时候都留下足够的“字节”。
TCP为你处理数据包丢失,这意味着它必须有缓冲区来保存你给予它的数据,直到它可以确认数据已经被另一方正确接收(通过TCP ACK)。没有缓冲区是无限的,因此必须有一个限制。这个限制是任意的,你可以选择任何你想要的,但你需要确保它足够大,以处理连接的BDP。
假设一个TCP套接字的缓冲区大小正好为:1字节。您尝试通过比特率为1 gbit/sec、单向延迟为1 ms的连接发送数据。
1.你给予TCP套接字你的第一个字节。
1.套接字阻止任何进一步的写调用(发送缓冲区已满)。

  1. TCP发送一个字节。千兆以太网适配器的批量传输速率为每字节8纳秒,因此传输时间可以忽略不计。
  2. 1毫秒后,接收器获得1个字节。
  3. 1毫秒后,您从接收器获得ACK。
  4. TCP从发送缓冲区中删除第一个字节,因为它已确认接收方正确获得了该字节。
    1.发送缓冲区解除阻塞,因为它有空间。
    1.你给予TCP套接字你的第二个字节...
    1.等等。
    这个连接传输数据的速度有多快?发送1个字节需要2毫秒,因此,这个连接的比特率为500字节/秒== 4 kbit/秒。
    哎呀。
    假设一个连接的速度为1G,平均单向延迟为10毫秒,往返时间(也就是套接字发送一个数据包到它收到该数据包的确认并知道要发送更多数据之间的时间)通常是延迟的两倍。
    因此,如果您有一个1千兆位的连接,RTT为20毫秒,那么该管道在完全使用的情况下,始终有1千兆位/秒 * 20毫秒== 2.5兆字节的数据在传输中。
    如果你的TCP发送缓冲区小于2.5兆字节,那么这个套接字将永远不会完全利用管道--你永远不会从套接字中获得千兆位/秒的性能。
    如果您的应用程序使用许多套接字,则所有TCP发送缓冲区的聚合大小必须为2.5 MB,以便充分利用这个假设的1千兆位/20毫秒RTT管道。例如,如果您使用8192字节的缓冲区,则需要306个并发TCP套接字来填充该管道。
    编辑问题:
    计算BDP只是将带宽乘以往返延迟,并注意单位。
    如果你有一个1千兆位/秒的连接,往返时间为20毫秒,那么会发生的是你乘以Bits/Sec * Seconds,所以秒数抵消了,剩下的是Bits。转换为Bytes,你就有了你的缓冲区大小。
  • 1千兆位/秒 * 20毫秒== 1 * 千兆位/秒 * 0.02秒==(1 * 0.02)千兆位
  • 0.020 gbit == 20 MBit。
  • 20 Mbit * 1字节/ 8位== 20 / 8 MB == 2.5 MB。

因此,我们的TCP缓冲区需要设置为2.5 MB,以使这个组成的管道饱和。

wqsoz72f

wqsoz72f3#

这取决于你的协议。如果你期望的消息超过8192字节,那么你应该相应地增加你的缓冲区大小。但是请记住,这个缓冲区大小只用于一次对Receive的调用。如果你真的想/需要,你可以在Receive上循环多次,并将接收到的数据复制到任意大的数据结构或缓冲区中。
另外请记住,重复调用Receive是一个很好的做法,直到您验证已经读取了给定消息的所有数据;即使单个消息小于缓冲区大小,单个Receive调用也可能无法检索到所有消息。

ar5n3qh5

ar5n3qh54#

与Microsoft无关,但我只是在使用TCP端口(而不是Unix域套接字)的C++线程echo服务器上进行实验,以查看吞吐量。在不同缓冲区大小的情况下对4M输入进行计时,得到以下结果:

1024 - real 0m0,102s; user  0m0,018s; sys   0m0,009s
2048 - real 0m0,112s; user  0m0,017s; sys   0m0,009s
8192 - real 0m0,163s; user  0m0,017s; sys   0m0,007s
 256 - real 0m0,101s; user  0m0,019s; sys   0m0,008s
  16 - real 0m0,144s; user  0m0,016s; sys   0m0,010s

看起来阅读1024字节的块减少了TCP开销,而处理时间(只是回显输入)不受缓冲区大小的影响。8192字节似乎很高,而真正的低值(如16)也不好。

w6mmgewl

w6mmgewl5#

8192将是理想的。如果你有超过这个大小的数据,你最好发送数据在恒定长度的数据包。
服务器发送的数据的大小可以使用WINSOCK中的recv函数来检查,该函数具有一个给出缓冲区长度的参数。

相关问题