В моей голове было несколько идей о том, как на самом деле содержать большое количество соединений с использованием архитектуры типа ввода-вывода при поддержке KISS. С помощью примеров в Интернете кажется, что большинство используют двойной / единственный связанный список с CONTAINING_RECORD. И, как новичок в IO-серверах (хотя совершенствуюсь каждый день), я тоже использую контейнер связанных списков для IO-архитектуры.
Мой вопрос, вместо того, чтобы использовать один / двойной связанный список для моих соединений, почему я не могу просто создать большой массив и использовать CONTAINING_RECORD? Можно ли использовать вектор STL? Будет ли это работать? Кроме того, каковы другие типы контейнеров, которые лучше всего работают с массивным сервером ввода-вывода.
Я нахожусь в процессе переписывания серверной архитектуры для моего игрового сервера (после многих ревизий), и на этот раз хотел бы двигаться в правильном направлении, потому что id, скорее всего, не придется переписывать его снова в ближайшем будущем.
Спасибо за ваше время и ответы.
Изменить: В настоящее время моя архитектура сервера (в двух словах):
Main thread listening and accepting -> Pass over the socket into a container.
Worker threads(2-3) grab IO events for the container of sockets.
Worker threads Read/Write Data on that container.
Основной поток и рабочие потоки используют связанный список. Я хочу уйти от этого.
Ваш «список соединений», вероятно, будет иметь удаления из любой позиции, а не только с конца. За std::vector
удаление элементов в середине — это операция O (N), но для связанных списков это может быть O (1). (Для односвязных списков это не тривиально и может потребовать неудобного API).
std::map
может быть интересным выбором, поскольку он предлагает как O(log N)
поиск и удаление элементов.
Как и во всех структурах данных, это очень сильно зависит от того, что вы хотите с ним делать.
В предыдущей работе я проводил большую часть своего времени, работая на чрезвычайно многопоточном сервере C ++, который в своем воплощении Windows использовал порты завершения ввода-вывода (в бэкэнде Solaris использовался / dev / poll, который не так уж отличается по нескольким элементам). Этот хранит связанные с соединением структуры данных в map
-подобная структура, датируемая до STL, с использованием файловых дескрипторов в качестве значений ключей. Таким образом, всякий раз, когда мы получаем событие в соединении, мы можем искать связанные с ним структуры данных по дескриптору, который нам передал уровень ввода-вывода. Новые соединения были просты в обращении — просто добавьте запись в словарь — и закрытые соединения также можно очистить довольно тривиально.
Естественно, нужно быть осторожным с многопоточным доступом к этим структурам и с упорядочением операций — поскольку ввод-вывод по своей сути эффективен, упорядочение операций имеет решающее значение. К счастью, IOCP не выдаст вам другое событие в другом потоке для того же сокета, пока вы не поместите сокет обратно в CP, но реализация Solaris должна была также сохранить структуру, связывающую дескрипторы файлов с рабочими потоками, чтобы гарантировать, что мы только обработали одно событие на сокет за раз и в строгом порядке, и мы также попытались внедрить последующие события для сокета в один и тот же поток, чтобы избежать необходимости переключать структуры сокетов на другой процессор, что является катастрофой для частоты обращений к кэшу.
Основное резюме, однако, заключается в том, что мы обнаружили, что специально разработанный класс словаря невероятно полезен для такого рода вещей.