Читаем О чём не пишут в книгах по Delphi полностью

Поле ClientAddr хранит строковое представление адреса клиента в виде "X.X.X.X: Port" — это поле используется только при выводе сообщений, связанных с данным клиентом. Поле ClientSocket содержит сокет, созданный для связи с данным клиентом. Поле Deleted необходимо для того, чтобы упростить удаление записей для тех клиентов, соединение с которыми уже потеряно. Список соединений хранится в глобальной переменной FConnections типа TList. Потеря соединения обнаруживается при попытке чтения или отправки данных через сокет. Если в одном цикле делать и попытки чтения, и удаление ненужных записей, этот цикл усложняется, и в нем легко сделать ошибку в индексах. Чтобы избежать этого, в "читающем" цикле те записи, для которых потеряно соединение, просто помечаются как удаленные с помощью свойства Deleted. Затем другой цикл удаляет все записи, помеченные для удаления.

После проверки новых подключений начинается проверка получения сообщений от тех клиентов, которые уже подключены. Для этого перебираются сокеты из списка подключений и для каждого вызывается select. Чтобы повысить производительность, сокеты проверяются не по одному, а группами. Как уже было сказано, множество типа TFDSet может содержать не более FD_SETSIZE сокетов, а в нашем списке их может оказаться больше. Приходится разбивать сокеты на группы размером по FD_SETSIZE и для каждой группы вызывать select отдельно.

Для тех сокетов, которые готовы к чтению, вызывается процедура ProcessSocketMessage. Ее код практически полностью совпадает с кодом одной итерации внутреннего цикла примера SimplestServer (см. листинг 2.15), т. е. процедура сначала читает размер строки, затем — саму строку, после этого формирует ответ и отправляет его клиенту. Реализуя эту функцию таким образом, мы пошли на некоторый риск блокировки, потому что функция select информирует только о том, что во входном буфере сокета есть хоть что-то, но вовсе не гарантирует, что там лежит уже все сообщение целиком. Наша же функция реализована таким образом, что она завершается либо после прочтения сообщения целиком, либо после обнаружения ошибки. Тем не менее в простых случаях можно пойти на такой риск, потому что, во-первых, короткие сообщения редко разбиваются на части, а во-вторых, если даже такое произойдет, оставшаяся часть сообщения, скорее всего, догонит первую достаточно быстро, и блокировка долгой не будет, так что риск при нормальной работе сети и клиента не очень велик.

Примечание

Эта ситуация отличается от использования select для UDP-сокетов. С ними такой проблемы не возникает, т. к. дейтаграмма никогда не приходит по частям, и если функция select показала готовность сокета. значит, уже получено все сообщение целиком.

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

Листинг 2.26. Основная часть сервера SelectServer

// Тайм-аут для функции select, хотя и передается через указатель,

// является для нее входным параметром, который не изменяется.

// Так как у нас везде будет использоваться один и тот же нулевой

// тайм-аут, можем один раз задать значение переменной Timeout

// и в дальнейшем всегда им пользоваться.

Timeout.tv_sec:= 0;

Timeout.tv_usec:= 0;

// Начало цикла подключения и общения с клиентами

repeat

 // Сначала проверяем, готов ли слушающий сокет.

 // Если он готов, это означает, что есть подключившийся,

 // но не обработанный функцией accept клиент

 FD_ZERO(SockSet);

 FD_SET(MainSocket, SockSet);

 if select(0, @SockSet, nil, nil, @Timeout) = SOCKET_ERROR then

raise ESocketException.Create('Ошибка при проверке готовности слушающего сокета: ' +

GetErrorString);

 // Если функция select оставила MainSocket в множестве, значит,

Перейти на страницу:

Похожие книги

1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных