我一直在研究TCP / IP IOCP服务器应用程序。
我一直在测试性能(这似乎与TCP吞吐量测试实用程序一致),现在一直在测试数据完整性 - 这就是我得到一些“怪异”的地方。
作为初始测试,我决定让测试客户端一遍又一遍地发送1MB数据块,其中该块只是一个接一个递增的整数序列。我的想法是,我可以验证每个接收到的数据缓冲区是否与该缓冲区中没有丢失数据一致,独立于接收到的任何其他缓冲区,这意味着我不需要担心线程处理已完成接收的顺序。 (为了验证,我提取缓冲区中的第一个整数并向前扫描,如果我遇到客户端发送的最大值,则将期望值重置为0.我还要检查以确保每个中收到的数据是4的倍数(因为它们是4字节整数))。
我似乎偶尔会从缓冲区中获取随机数据块,值会逐渐增加1,然后会跳过一堆。代码虽然看起来很简单,但并不多。我最初在Delphi中编写了测试但是在遇到这些问题后,我在Visual Studio 2010 C ++中重写了一个版本,似乎有同样的问题(或者至少非常相似)。
在真实系统中显然有更多的代码,但是我可以在工作线程中将其简化为几乎只处理已完成的接收,验证缓冲区中的数据然后再次发布它们。在我最初接受连接之后,我创建了两个重叠结构并为每个分配了1MB缓冲区,然后为每个结构调用WSARecv。我已经仔细检查过,我不小心在两者之间共享相同的缓冲区。然后,以下几乎是重用它们的原因:
DWORD numberOfBytesTransferred = 0;
ULONG_PTR completionKey = NULL;
PMyOverlapped overlapped = nullptr;
while (true)
{
auto queueResult = GetQueuedCompletionStatus(iocp, &numberOfBytesTransferred, &completionKey, (LPOVERLAPPED *)&overlapped, INFINITE);
if (queueResult)
{
switch (overlapped->operation)
{
case tsoRecv:
{
verifyReceivedData(overlapped, numberOfBytesTransferred); // Checks the data is a sequence of incremented integers 1 after the other with no gabs
overlapped->overlapped = OVERLAPPED(); // Reset the OVERLAPPED structure to defaults
DWORD flags = 0;
numberOfBytesTransferred = 0;
auto returnCode = WSARecv(socket, &(overlapped->buffer), 1, &numberOfBytesTransferred, &flags, (LPWSAOVERLAPPED) overlapped, nullptr);
break;
}
default:;
}
}
}
也许我在上面的简单测试中没有处理某种错误或其他信息?我最初有一个IOCP客户端发送数据,但在Delphi中使用Indy阻塞套接字写了另一个非常简单的客户端。一旦连接,它基本上就是一行代码。
while true do
begin
IdTCPClient.IOHandler.WriteDirect(TIdBytes(BigData), Length(BigData));
end;
我还使用不同的异步套接字组件编写了另一个服务器,并且我没有检测到接收到的数据的问题,至少我上面的IOCP示例。我可以发布更多的代码和可能的版本进行编译,但我想如果我错过了一些明显的东西,我会发布上面的内容。我认为每个插槽使用一个接收和一个发送工作正常,但我的理解是,发布多个接收以提高性能是有效的。
答案 0 :(得分:1)
我相信这已经解决了 - 我的大多数假设和代码都是正确的,但是对于特定套接字来说,似乎不能同时从多个线程调用WSASend或WSARead。对于特定套接字,可以有多个未完成的发送和接收调用,但启动它们的实际调用需要使用临界区(或类似)进行序列化。这是我对MSDN文档的一点误解,我认为它可以完成,但是如果没有一些额外的同步你就不会知道哪个缓冲区会被填充(而我的测试并不关心哪个首先被填充)。它看起来根本不安全,除非一次调用一个并且可能导致缓冲区内的数据损坏。
我改变的唯一代码是为每个连接添加一个关键部分以保护对这些的调用,到目前为止没有任何问题。我认为可以单独保护WSASend和WSARecv,但还没有测试过。
我发布了一个与此here相关的更深入的问题,其中包含更多代码示例。
答案 1 :(得分:-1)
取出循环。您已经检查了收到的数据并安排了一个新的异步读取,当读取完成时,它将或应该重新输入此代码。循环完全不正确。