TCP / IP сервер с использованием IOCP. Случайное повреждение данных в приемных буферах

Я работал над приложением сервера TCP / IP IOCP.

Я тестировал производительность (что, похоже, соответствует утилитам тестирования пропускной способности TCP) и сейчас тестирую целостность данных — вот где я получаю некоторую «странность».

В качестве начального теста я решил, чтобы тестовый клиент снова и снова отправлял блок данных размером 1 МБ, где этот блок представляет собой просто последовательность целых чисел, увеличенных один за другим. Идея заключается в том, что я могу проверить, что каждый полученный буфер данных не содержит отсутствующих данных в этом буфере, независимо от любого другого полученного буфера, то есть мне не нужно беспокоиться о том, какие потоки порядка обрабатывают завершенные приемы. (чтобы проверить, я извлекаю первое целое число из буфера и сканирую вперед, сбрасываю ожидаемое значение в 0, если сталкиваюсь с максимальным значением, которое отправляет клиент. У меня также есть проверка, чтобы убедиться, что данные, полученные в каждом, кратны 4 (так как они являются 4-байтовыми целыми числами)).

Я, кажется, получаю случайные блоки данных, которые иногда пропадают из буфера, значения возрастут 1 к 1, а затем будет пропущена куча. Код кажется довольно простым и не очень. Первоначально я написал тест в Delphi, но после возникновения этих проблем я переписал версию в Visual Studio 2010 C ++ и, похоже, столкнулся с той же проблемой (или, по крайней мере, очень похожей).

Очевидно, что в реальной системе больше кода, но я могу свести его к значительной степени в рабочем потоке, который просто обрабатывает завершенные приемы, проверяет данные в буфере и затем отправляет их снова. После того, как я первоначально принимаю соединение, я создаю две перекрывающиеся структуры и выделяю по 1 МБ буферов для каждого, а затем вызываю 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, по крайней мере, пока. Я могу опубликовать больше кода и, возможно, версию для компиляции, но подумал, что выложу вышеизложенное на случай, если пропустил что-то очевидное. Я думаю, что использование одного приема и одной отправки на сокет работает нормально, но, насколько я понимаю, можно публиковать более одного сообщения для повышения производительности.

3

Решение

Я считаю, что это решено — большинство моих предположений и кода были правильными, однако кажется, что для определенного сокета не может быть одновременных вызовов из нескольких потоков в WSASend или WSARead. Может быть несколько невыполненных вызовов как для отправки, так и для получения для конкретного сокета, но фактические вызовы для их инициирования должны быть сериализованы с критической секцией (или аналогичной). Это было небольшое неправильное понимание документации MSDN с моей стороны, я думал, что это можно сделать, но вы не знаете, какой буфер будет заполнен первым без какой-либо дополнительной синхронизации (и мой тест не заботился о том, который был заполнен первым). Похоже, что это просто небезопасно, если только вызовы не выполняются по одному и могут привести к повреждению данных в буферах.

Единственный код, который я изменил, — это добавить критический раздел для каждого соединения, чтобы защитить вызовы к ним, и до сих пор не было проблем. Я думаю, что было бы возможно защитить WSASend и WSARecv по отдельности, но еще не проверял это.

Я разместил гораздо более глубокий вопрос, связанный с этим Вот это имеет больше примеров кода.

1

Другие решения

Выньте петлю. Вы уже проверяете полученные данные и планируете новое асинхронное чтение, которое будет или должно повторно ввести этот код, когда чтение завершится. Цикл совершенно неверный.

-1

По вопросам рекламы [email protected]