- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
begin
LogMessage('Ошибка в событии FD_WRITE: ' +
GetErrorString(NetEvents.iErrorCode[FD_WRITE_BIT)));
Break;
end;
// Отправляем то, что лежит в буфере
if not DoSendBuf then Break;
end;
if NetEvents.lNetworkEvents and FD_CLOSE <> 0 then
begin
// Клиент закрыл соединение
if NetEvents.iErrorCode[FD_CLOSE_BIT] <> 0 then
begin
LogMessage('Ошибка в событии FD_CLOSE: ' +
GetErrorString(NetEvents.iErrorCode[FD_CLOSE_BIT]));
Break;
end;
LogMessage('Клиент закрыл соединение');
shutdown(FSocket, SD_BOTH);
Break;
end;
end;
WSA_WAIT_FAILED: begin
LogMessage('Ошибка при ожидании сообщения: ' + GetErrorString);
Break;
end;
else begin
LogMessage(
'Внутренняя ошибка сервера — неверный результат ожидания ' +
IntToStr(WaitRes));
Break;
end;
end;
until False;
closesocket(FSocket);
LogMessage('Нить остановлена');
end;
// Функция возвращает True, если нить завершилась
function TClientThread.GetFinished: Boolean;
begin
// Ждем окончания работы нити с нулевым тайм-аутом.
// Если нить завершена, вернется WAIT_OBJECT_0.
// Если еще работает, вернется WAIT_TIMEOUT.
Result := WaitForSingleObject(Handle, 0) = WAIT_OBJECT_0;
end;
// Метод для остановки нити извне.
// Взводим соответствующее событие, а остальное сделаем
// при обработке события
procedure TClientThread.StopThread;
WSASetEvent(FEvents[0]);
end;
Модуль WinSock2_Events, появившийся в списке uses, содержит объявления констант, типов и функций из WinSock 2, которые понадобятся в программе. Модуль ShutdownConst содержит объявления констант для функции shutdown, которые отсутствуют в модуле WinSock Delphi 5 и более ранних версиях — этот модуль нам понадобился, чтобы программу можно было откомпилировать в Delphi 5.
Нить использует три события, дескрипторы которых хранятся в массиве FEvents. Событие FEvents[0] служит для уведомления нити о том, что необходимо завершиться, FEvents[1] — для уведомления о том, что нужно оправить данные, FEvents[2] связывается с событиями на сокете. Такой порядок выбран не случайно. Если взведено несколько событий, функция WSAWaitForMultipleEvents вернет результат, соответствующий событию с самым младшим из взведенных событий индексом. Соответственно, чем ближе к началу массива, тем более высокий приоритет у события. Событие, связанное с сокетом, имеет наинизший приоритет для того, чтобы повысить устойчивость сервера к DoS-атакам. Если приоритет этого события был бы выше, чем события остановки нити, то в случае закидывания сервера огромным количеством сообщений от клиента, событие FD_READ было бы всегда взведено, и сервер все время тратил бы на обработку этого события, игнорируя сигнал об остановке нити. Соответственно, сигнал об остановке должен иметь самый высокий приоритет, чтобы остановке нити ничего не могло помешать. Тем, как отправляются сообщения, сервер управляет сам. поэтому не приходится ожидать проблем, связанных с тратой излишних ресурсов на обработку сигнала отправки. Соответственно, этому событию присваивается приоритет, промежуточный между событием остановки нити и событием сокета.
Так как клиент по новому протоколу перед отправкой сообщения не обязан ждать, пока сервер ответит на предыдущее, возможны ситуации, когда ответ на следующее сообщение сервер должен готовить уже тогда, когда предыдущее еще не отправлено. Кроме того, сервер может отправить сообщение по собственной инициативе, и этот момент тоже может наступить тогда, когда предыдущее сообщение еще не отправлено. Таким образом, мы вынуждены формировать очередь сообщений в том или ином виде. Так как протокол TCP, с одной стороны, может объединять несколько пакетов в один, а с другой, не обязан отправлять отдельную строку за один раз, проще всего не делать очередь из отдельных строк, а заранее объединять их в одном буфере и затем пытаться отправить все содержимое буфера. Таким буфером в нашем случае является поле FSendBuf, метод SendString добавляет строку в этот буфер, a DoSendBuf отправляет данные из этого буфера. Если все данные отправить за один раз не удалось, отправленные данные удаляются из буфера, а оставшиеся будут отправлены при следующем вызове SendBuf. Все операции с буфером FSendBuf выполняются внутри критической секции, т.к. функция SendString может вызываться из других нитей. К каждой строке добавляется символ #0, который, согласно протоколу, является для клиента разделителем строк в потоке.
Сигналом к отправке данных является событие FEvents[1]. Метод SendString, помещая данные в буфер, взводит это событие. Если все содержимое буфера за один раз отправить не удастся, то через некоторое время возникнет событие FD_WRITE, означающее готовность сокета к приему новых данных. Это событие привязано у нас к FEvents[2], поэтому при наступлении FEvents[2] тоже возможна отправка данных.
Для приема данных здесь также используется буфер. Прямой необходимости в этом нет — можно было, как и раньше, помещать данные непосредственно в переменную, хранящую длину строки, а затем и в саму строку. Сделано это в учебных целях, чтобы показать, как можно работать с подобным буфером. Буфер имеет фиксированный размер. Сначала мы читаем из сокета в этот буфер столько, сколько сможем, а потом начинаем разбирать полученное точно так же, как и раньше, копируя данные то в целочисленную, то в строковую переменную. Когда строковая переменная полностью заполняется, строка считается принятой, для пользователя выводится ответ на нее, а в буфер для отправки добавляется ответная строка. Достоинством такого способа является то, что, с одной стороны, за время обработки одного события сервер может прочитать несколько запросов от клиента (если буфер достаточно велик), но, с другой стороны, это не приводит к зацикливанию, если сообщения поступают непрерывно. Другими словами, разработчик здесь сам определяет, какой максимальный объем данных можно получить от сокета за один раз. Иногда это бывает полезно.
Теперь рассмотрим нить, обслуживающую слушающий сокет. Код этой нити приведен в листинге 2.64.
Листинг 2.64. Код нити, обслуживающей слушающий сокетunit ListenThread;
{
Нить, следящая за подключением клиента к слушающему сокету.
При обнаружении подключения она создает новую нить для работы с подключившимся клиентом, а сама продолжает обслуживать "слушающий" сокет.
}
interface
uses
SysUtils, Classes, WinSock, WinSock2_Events;
type
TListenThread = class(TThread)
private
// Сообщение, которое нужно добавить в лог.
// Хранится в отдельном поле, т.к. метод, вызывающийся
// через Synchronize, не может иметь параметров.
FMessage: string;
// Сокет, находящийся в режиме прослушивания
FServerSocket: TSocket;
// События нити
// FEvents[0] используется для остановки нити
// FEvents[1] связывается с событием FD_ACCEPT
FEvents: array[0..1] of TWSAEvent;
// Список нитей клиентов
FClientThreads: TList;
// Если True, сервер посылает клиенту сообщения
// по собственной инициативе
FServerMsg: Boolean;
// Вспомогательный метод для вызова через Synchronize
procedure DoLogMessage;
protected
procedure Execute; override;
// Вывод сообщения в лог главной формы
procedure LogMessage(const Msg: string);
public
constructor Create(ServerSocket: TSocket; ServerMsg: Boolean);
destructor Destroy; override;
// Вызывается извне для остановки сервера
procedure StopServer;
end;
implementation
uses
MainServerUnit, ClientThread;
{ TListenThread }
// "Слушающий" сокет создается в главной нити,
// а сюда передается через параметр конструктора
constructor TListenThread.Create(ServerSocket: TSocket; ServerMsg: Boolean);
begin
FServerSocket := ServerSocket;
FServerMsg := ServerMsg;
// Создаем события
FEvents[0] := WSACreateEvent;
if FEvents[0] = WSA_INVALID_EVENT then
raise ESocketError.Create(
'Ошибка при создании события для сервера:' + GetErrorString);
FEvents[1] := WSACreateEvent;

