Обработка тайм-аутов на сокет-сервере - PullRequest
3 голосов
/ 29 сентября 2011

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

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

Любые предложения / идеи будут с благодарностью.Спасибо.

Ответы [ 3 ]

2 голосов
/ 29 сентября 2011

Если это новый проект или вы открыты для серьезного рефакторинга вашего проекта, взгляните на Reactive Extensions. Rx имеет элегантное решение для тайм-аутов в асинхронных вызовах:

var getBytes = Observable.FromAsyncPattern<byte[], int, int, int>(_readStream.BeginRead, _readStream.EndRead);

getBytes(buffer, 0, buffer.Length)
     .Timeout(timeout);

Обратите внимание, что приведенный выше код просто предназначен для демонстрации того, как тайм-аут в Rx, и он будет более сложным в реальном проекте.

По производительности я бы не сказал, если вы не профилируете свой конкретный вариант использования. Но я видел разговор, в котором они использовали Rx в сложной платформе, управляемой данными (вероятно, как ваши требования), и они сказали, что их программное обеспечение способно принимать решения менее чем за 30 мс.

С точки зрения кода, я считаю, что использование Rx делает мой код более элегантным и менее многословным.

2 голосов
/ 29 сентября 2011

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

Другой подход, который работалотлично для меня использовалась очередь жетонов активности.Давайте используем C #:

class token {
    ClientContext theClient; // this is the client we've observed activity on
    DateTime  theTime;       // this is the time of observed activity
};

class ClientContext {
    // ... what you need to know about the client
    DateTime lastActivity;   // the time the last activity happened on this client
}

Каждый раз, когда на конкретном клиенте происходит действие, токен генерируется и помещается в очередь FIFO, а lastActivity обновляется в ClientContext.

.Есть еще один поток, который выполняет следующий цикл:

  • извлекает самый старый токен из очереди FIFO;
  • проверяет, соответствует ли theTime в этом токене theClient.lastActivity;
  • если это так, выключает клиента;
  • просматривает следующий самый старый токен в очереди, вычисляет, сколько времени осталось до его отключения;
  • Thread.Sleep(<this time>);
  • repeat

Цена такого подхода небольшая, но постоянная, т. Е. O (1) накладные расходы.Можно найти более быстрые решения в лучшем случае, но мне кажется, что трудно найти решение с более быстрой производительностью в худшем случае.

0 голосов
/ 29 сентября 2011

Я думаю, что легче контролировать форму тайм-аута в потоке соединения. Так что у вас может быть что-то вроде этого:

// accept socket and open stream
stream.ReadTimeout = 10000; // 10 seconds

while (true)
{
    int bytes = stream.Read(buffer, 0, length)
    // process the data
}

Вызов stream.Read() либо вернет данные (что означает, что клиент жив), либо сгенерирует исключение ввода-вывода (аварийное отключение), либо вернет 0 (клиент закрыл сокет).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...