套接字recv()然后使用overlapped_io与FILE_NO_BUFFERING_FLAG写入文件时数据对齐

时间:2014-05-07 06:26:04

标签: c++ sockets recv overlapped-io

我编写的C ++程序只是从另一台计算机接收数据,并将数据写入具有高吞吐量的SSD RAID(自GbEthernet以来约为100MB / s)。

我已经设置了2 overlapped_io,它们从以太网接收并写入SSD。

接收完成后,它会向作者发送消息。

我在磁盘上创建文件时使用FILE_NO_BUFFERING_FLAG

在网络发件人方面,我使用重叠的IO来发送数据。

我遇到了问题:当从套接字收到时,rv = recv()未与磁盘对齐(可能是4096次?)。

我该怎么办?

1 个答案:

答案 0 :(得分:0)

recv和无缓冲的写入彼此之间并不是非常兼容。可以让它工作,但需要额外的工作。

执行无缓冲写入时,两者缓冲区的起始地址和写入量必须是扇区大小的倍数(请参阅MSDN)。对齐缓冲区是微不足道的,但是处理recv可以返回几乎每个数据量的事实(达到你要求的数量,但理论上它可能只有1个字节)是有点工作的。

另一个问题是虽然几乎可以保证扇区大小是2的幂(尽管至少在<20世纪90年代,习惯于存在具有非二次幂扇区的硬盘) ,这个事实被控制器隐藏了。你不知道它是什么。即使你知道,在下一台计算机上可能会有所不同。它可能是512或1024或其他东西。

如何处理?大多数程序员只需使用一个分配完整内存页面的函数,例如VirtualAlloc或匿名内存映射。由于它们在页面上运行,因此它们必须是页面大小对齐的,这通常意味着4096字节 1

由于要写入的数据量也必须是扇区大小的倍数(但接收的数据量可能不是),因此您需要向下舍入,进行部分写入,并保留其余部分用于下一次写 同样,问题是您不知道扇区大小,因此您可以做的最好的事情是向下舍入到您用于缓冲区启动的相同粒度(其他任何事情都是荒谬的)。换句话说,你在概念上必须做这样的事情:

while(rv < 0xffff)          // don't have enough yet
    receive_more_and_append();

num_write = rv &  ~0xffff;
rv -= num_write;
memcpy(other_buf, buf+num_write, rv);
WriteFileEx(...);

<小时/> 1 这只是事实的一半,因为Windows的最小分配粒度为64kB。您不能分配小于64k的东西,并且它不能小于64k。所以事实上,对于高达64k的行业来说,你是有利的,这比你可能遇到过的任何东西都要大。 此外,作为一个小的挑剔,Itanium有8k页,而不是4k - 但这没有问题,它实际上更好。