Каков наилучший способ реализовать эхо-сервер с асинхронным вводом-выводом и IOCP?

Как мы все знаем, эхо-сервер - это сервер, который читает данные из сокета и записывает эти данные в другой сокет.

Так как порты завершения ввода / вывода Windows предоставляют различные способы работы, мне было интересно, как лучше (наиболее эффективный) реализовать эхо-сервер. Я обязательно найду кого-то, кто проверил способы, которые я опишу здесь, и может внести свой вклад.

Мои занятия Stream который абстрагирует сокет, именованный канал или что-то еще, и IoRequest который абстрагирует как OVERLAPPED структура и буфер памяти для ввода / вывода (конечно, подходит как для чтения, так и для записи). Таким образом, когда я выделяю IoRequest Я просто выделяю память для памяти, буфер для данных + OVERLAPPED структура в один выстрел, поэтому я называю malloc() только однажды. В дополнение к этому, я также реализую необычные и полезные вещи в IoRequest объект, такой как атомный счетчик ссылок и т. д.

Сказал, что давайте рассмотрим способы сделать лучший эхо-сервер:

-------------------------------------------- Метод А. --- ---------------------------------------

1) Сокет "читатель" завершает чтение, обратный вызов IOCP возвращается, и у вас есть IoRequest только что закончил с буфером памяти.

2) Давайте скопируем только что полученный буфер с "ридером" IoRequest "писателю" IoRequest, (это будет включать memcpy() или что угодно).

3) Давайте снова запустим новое чтение с ReadFile() в "ридере", с тем же IoRequest используется для чтения.

4) Давайте запустим новое письмо с WriteFile() в "писатель".

-------------------------------------------- Метод Б. --- ---------------------------------------

1) Сокет "читатель" завершает чтение, обратный вызов IOCP возвращается, и у вас есть IoRequest только что закончил с буфером памяти.

2) Вместо того, чтобы копировать данные, передайте это IoRequest "писателю" для записи, без копирования данных с memcpy(),

3) "Читателю" теперь нужен новый IoRequest чтобы продолжить чтение, выделить новый или передать уже выделенный ранее, возможно, только что завершенный для записи до того, как произойдет новое.


Итак, в первом случае каждый Stream объекты имеют свои IoRequest, данные копируются с memcpy() или аналогичные функции, и все работает нормально. Во втором случае 2 Stream объекты проходят IoRequest объекты друг друга, без копирования данных, но это немного сложнее, вы должны управлять "обменом" IoRequest объекты между 2 Stream объекты, с возможным недостатком, чтобы получить проблемы с синхронизацией (что относительно того завершения происходят в разных потоках?)

Мои вопросы:

Q1) Стоит ли избегать копирования данных!? Копирование 2 буферов с memcpy() или похожий, очень быстрый, также потому, что кеш процессора используется именно для этой цели. Давайте рассмотрим, что с первым методом у меня есть возможность выводить из гнезда "reader" в несколько сокетов "writer", но со вторым я не могу этого сделать, так как я должен создать N new IoRequest объекты для каждого N авторов, так как каждый WriteFile() нужен свой OVERLAPPED состав.

Q2) Я предполагаю, что когда я запускаю новые N писем для N различных сокетов с WriteFile()Я должен предоставить N разных OVERLAPPED Структура И N разных буферов, где читать данные. Или я могу уволить N WriteFile() звонки с N разные OVERLAPPED брать данные из того же буфера для N сокетов?

1 ответ

Решение

Стоит ли избегать копирования данных!?

Зависит от того, сколько вы копируете. 10 байт, не так много. 10 МБ, то да, стоит избегать копирования!

В этом случае, поскольку у вас уже есть объект, который содержит данные rx и блок OVERLAPPED, копировать его кажется бессмысленным - просто переиздайте его в WSASend() или что-то еще.

but with the second one I can't do that

Вы можете, но вам нужно абстрагировать класс 'IORequest' от класса 'Buffer'. В буфере хранятся данные, атомный подсчет ссылок int и любая другая информация управления для всех вызовов, IOrequest блок OVERLAPPED и указатель на данные и любую другую информацию управления для каждого вызова. Эта информация может иметь атомный int-подсчет ссылок для объекта буфера.

IOrequest - это класс, который используется для каждого звонка отправки. Поскольку он содержит только указатель на буфер, копировать данные не нужно, поэтому он достаточно мал и O(1) соответствует размеру данных.

Когда приходят завершения tx, потоки обработчика получают запрос IOrequest, разыменовывают буфер и переводят атомарный int в него в ноль. Поток, которому удается достичь 0, знает, что буферный объект больше не нужен, и может удалить его (или, что более вероятно, на высокопроизводительном сервере, повторно его переименовать для последующего повторного использования).

Или я могу запустить N вызовов WriteFile() с N различными OVERLAPPED, получая данные из одного и того же буфера для N сокетов?

Да, ты можешь. Смотри выше.

Число рейнольдса многопоточность - конечно, если ваши "данные управления" могут быть получены из нескольких потоков обработчика завершения, тогда да, вы можете защитить их критическим разделом, но атомарный int должен подойти для пересчета буфера.

Другие вопросы по тегам