Неблокирующие проблемы с буфером TCP - PullRequest
1 голос
/ 12 июня 2010

Я думаю, что у меня проблема. У меня есть два приложения TCP, подключенных друг к другу, которые используют порты завершения ввода / вывода winsock для отправки / получения данных (неблокирующие сокеты).

Все работает отлично, пока не произойдет разрыв передачи данных. Отправитель начинает отправку неверных / искаженных данных.

Я выделяю буферы, которые я отправляю, в стеке, и если я правильно понимаю, это неправильно, потому что эти буферы должны оставаться такими, какими я их отправлял, до тех пор, пока я не получу уведомление IOCP о завершении записи.

Возьмем для примера:

void some_function()
{
    char cBuff[1024];

    // filling cBuff with some data

    WSASend(...); // sending cBuff, non-blocking mode

    // filling cBuff with other data

    WSASend(...); // again, sending cBuff

    // ..... and so forth!
}

Если я правильно понимаю, каждый из этих вызовов WSASend () должен иметь свой собственный уникальный буфер, и этот буфер можно использовать повторно только после завершения отправки.
Правильно?

Теперь, какие стратегии я могу реализовать для поддержания большого количества таких буферов, как я должен их обрабатывать, как я могу избежать снижения производительности и т. Д. '?
И, если я хочу использовать буферы, это означает, что я должен скопировать данные, которые будут отправлены из исходного буфера, во временный, таким образом, я бы установил SO_SNDBUF на каждом сокете на ноль, чтобы система не переписывала то, что я уже скопировано. Ты со мной? Пожалуйста, дайте мне знать, если я не был ясен.

Ответы [ 3 ]

3 голосов
/ 13 июня 2010

Серьезно посмотрите на boost::asio.Асинхронный ввод-вывод является его специализацией (как и следует из названия). Это довольно зрелая библиотека, теперь она находится в Boost с 1.35.Многие люди используют его в производстве для очень интенсивных сетей.В документации множество примеров .

Одна вещь наверняка - очень серьезно относится к работе с буферами .

Редактировать:

Основная идея обработки пакетов ввода - организация очередей .

  • Создание, скажем, трех связанных списков предварительно распределенных буферов - один для free буфера, один для данных для обработки (полученных) данных, один для данных для отправки данных.
  • Каждый раз, когда вам нужно что-то отправить- удалить буфер из списка free (выделить новый, если свободный список пуст), заполнить данными, поместить его в список для отправки .
  • Каждый раз, когда вам нужно что-то получить - уберите буфер из списка free , как указано выше, передайте его в процедуру приема ввода-вывода.
  • Периодически снимайте буферы с , чтобы бытьотправлено очередь, передайте их для отправки процедуры.
  • При завершении отправки (встроенный или асинхронный) - вернуть их в список free .
  • По окончании приема - поместите буфер в список для обработки .
  • Сделайте так, чтобы ваша "бизнес" подпрограмма убрала буферы из списка для обработки .

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

1 голос
/ 12 июня 2010

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

Точно так же я не думаю, что это хорошая идея изменить буфер до окончания отправки.

Я был бы склонен хранить данные в какой-то очереди. Один поток может продолжать добавлять данные в очередь. Второй поток может работать в цикле. Сделайте отправку и дождитесь ее окончания. Если есть еще данные, сделайте еще одну отправку, иначе ждите больше данных.

Вам понадобится критическая секция (или некоторая такая), чтобы приятно разделить очередь между потоками и, возможно, событие или семафор для потока-отправителя, чтобы ждать, если нет готовых данных.

0 голосов
/ 12 июня 2010

Теперь, какие стратегии я могу реализовать для поддержания большого количества таких буферов, как я должен с ними обращаться, как я могу избежать снижения производительности и т. Д. '?

Трудно узнать ответ, не зная больше о вашем конкретном дизайне. В общем, я бы избегал поддерживать свой собственный «мешок буферов» и вместо этого использовал бы встроенный в ОС мешок буферов - кучу.

Но в любом случае, что бы я сделал в общем случае, это выставил бы интерфейс для вызывающих программ вашего кода, который отражает то, что WSASend делает для перекрывающегося ввода-вывода. Например, предположим, что вы предоставляете интерфейс для отправки определенной структуры:

struct Foo
{
   int x;
   int y;
};

// foo will be consumed by SendFoo, and deallocated, don't use it after this call
void SendFoo(Foo* foo);

Я бы потребовал, чтобы пользователи SendFoo выделяли экземпляр Foo с новым, и говорили им, что после вызова SendFoo память больше не «принадлежит» их коду, и поэтому они не должны ее использовать.

Вы можете применить это еще больше с небольшой хитростью:

// After this operation the resultant foo ptr will no longer point to
// memory passed to SendFoo
void SendFoo(Foo*& foo);

Это позволяет телу SendFoo отправлять адрес памяти в WSASend, но изменять передаваемый указатель на NULL, разрывая связь между кодом вызывающего абонента и его памятью. Конечно, вы не можете точно знать, что звонящий делает с этим адресом, у него может быть копия в другом месте.

Этот интерфейс также обеспечивает использование одного блока памяти с каждым WSASend. Вы действительно вступаете в более чем опасную территорию, пытаясь разделить один буфер между двумя вызовами WSASend.

...