- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
// Сразу выделяем память под строку
SetLength(Connection.Msg, Connection.MsgSize);
end;
end
elsе if Res = 0 then
begin
AddMessageToLog('Клиент ' + Connection.ClientAddr +
' закрыл соединение');
RemoveConnection;
Exit;
end
else
// Ошибку WSAEWOULDBLOCK игнорируем, т.к. она говорит
// только о том, что входной буфер сокета пуст, но в целом
// все в порядке - такое вполне возможно при ложных
// срабатываниях сообщения
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
AddMessageToLog('Ошибка при получении данных от клиента ' +
Connection.ClientAddr + ': ' + GetErrorString);
RemoveConnection;
Exit;
end;
end
else if Connection.Phase = tpReceiveString then
begin
// Следующий этап - чтение строки. Он практически не отличается
// по реализации от этапа чтения длины строки, за исключением
// того, что теперь буфером, куда помещаются полученные от
// клиента данные, служит не Connection.MsgSize,
// a Connection.Msg.
Res :=
recv(Connection.ClientSocket, Connection.Msg(Connection.Offset + 1),
Connection.BytesLeft, 0);
if Res > 0 then
begin
Inc(Connection.Offset, Res);
Dec(Connection.BytesLeft, Res);
// Если количество оставшихся байтов равно нулю, можно
// переходить к следующему этапу.
if Connection.BytesLeft = 0 then
begin
AddMessageToLog('От клиента ' + Connection.ClientAddr +
' получена строка: ' + Connection.Msg);
// Преобразуем строку. В отличие от предыдущих примеров,
// здесь мы явно добавляем к строке #0. Это связано с тем,
// что при отправке, которая тоже может быть выполнена не
// за один раз, мы указываем индекс того символа строки,
// начиная с которого нужно отправлять данные. И (хотя
// теоретически вероятность этого очень мала) может
// возникнуть ситуация, когда за один раз будут отправлены
// все символы строки, кроме завершающего #0, и тогда при
// следующей отправке начинать придется с него. Если мы
// будем использовать тот #0, который добавляется к концу
// строки автоматически, то в этом случае индекс выйдет за
// пределы диапазона. Поэтому мы вручную добавляем ещё один
// #0 к строке, чтобы он стал законной ее частью.
Connection.Msg :=
AnsiUpperCase(StringReplace(Connection.Msg, #0, '#0', [rfReplaceAll])) +
'(AsyncSelect server)'#0;
// Следующий этап - отправка строки клиенту
Connection.Phase := tpSendString;
// Отправлено на этом этапе 0 байт
Connection.Offset := 0;
// Осталось отправить Length(Connection.Msg) байтов.
// Единицу к длине строки, в отличие от предыдущих
// примеров, не добавляем, т.к. там эта единица нужна была
// для того, чтобы учесть добавляемый к строке
// автоматически символ #0. Здесь мы еще один #0 добавили
// к строке явно, поэтому он уже учтен в функции Length.
Connection.BytesLeft := Length(Connection.Msg);
// Ставим в очередь сообщение с событием FW_WRITE.
// Его получение заставит сервер отправить данные
PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_WRITE);
end;
end
else if Res = 0 then
begin
AddMessageToLog('Клиент ' + Connection.ClientAddr +
' закрыл соединение');
RemoveConnection;
Exit;
end
elsе
// Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
AddMessageToLog('Ошибка при получении данных от клиента ', +
Connection.ClientAddr + ': ' + GetErrorString);
RemoveConnection;
Exit;
end;
end
else if Connection.Phase = tpSendString then
// Если сервер находится на этапе отправки данных,
// а событие FD_READ все же произошло, отмечаем это
Connection.SendRead := True;
end;
FD_WRITE: begin
if Connection.Phase = tpSendString then
begin
// При наступлении события FD_WRITE проверяем, находится ли
// сервер на этапе отправки данных, и если да, отправляем их
Res :=
send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],
Connection.BytesLeft, 0);
if Res > 0 then
begin
Inc(Connection.Offset, Res);
Dec(Connection.BytesLeft, Res);
// Если Connections. BytesLeft = 0, значит, строка отправлена
// полностью.
if Connection.BytesLeft = 0 then
begin
AddMessageToLog('Клиенту ' + Connection.ClientAddr +
' отправлена строка: ' + Connection.Msg);
// Очищаем строку, просто чтобы сэкономить память
Connection.Msg := '';
// Следующий этап - снова получение длины строки от клиента
Connection.Phase := tpReceiveLength;
// Получено - 0 байт
Connection.Offset := 0;
// Осталось прочитать столько, сколько занимает целое число
Connection.BytesLeft := SizeOf(Integer);
// Если были промежуточные события FD_READ, вызываем их
// снова искусственно
it Connection.SendRead then
begin
PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_READ);
Connection.SendRead := False;
end;
end;
end
else if WSAGetLastError <> WSAEWOULDBLOCK then
begin
AddMessageToLog('Ошибка при отправке данных клиенту ' +
Connection.ClientAddr + ': ' + GetErrorString);
RemoveConnection;
Exit;
end;
end;
end;
FD_CLOSE: begin
// Клиент вызвал функцию shutdown. Закрываем соединение.
AddMessageToLog('Клиент ' + Connection.ClientAddr +
' закрыл соединение');
shutdown(Connection.ClientSocket, SD_BOTH);
RemoveConnection;
end
else
begin
AddMessageToLog('Неверное событие при обмене с клиентом ' +
Connection.ClientAddr);
RemoveConnection;
end;
end;
end;
В этом примере можно найти много общего с кодом из листинга 2.32 — получение и отправка данных в зависимости от этапа выполняется практически одинаково, различаются только условия, при которых эти участки кода выполняются. Обратите внимание, что теперь проверка того, какой этап чтения выполняется, сделана взаимоисключающей, т.е. при обработке одного сообщения не может быть прочитана и длина строки, и сама строка. Это сделано, чтобы убрать ложные срабатывания. Рассмотрим два возможных варианта. Первый вариант — когда во входном буфере сокета оказывается сразу длина и строка (или ее часть). После того как будет прочитана длина, сообщение WM_SOCKETMESSAGE с параметром FD_READ вновь будет помещено в очередь, поскольку функция recv помещает это сообщение в очередь, если после ее вызова во входном буфере сокета остались данные. Если мы немедленно перейдем ко второму этапу, то прочитаем из буфера сокета все оставшиеся там данные, но сообщение в очереди все равно останется, что даст нам ложное срабатывание, когда петля сообщений извлечет и диспетчеризует это сообщение. Таким образом, выполнение сразу двух этапов при обработке одного сообщения не даст выигрыша в производительности, т.к. все равно придется извлекать и обрабатывать два сообщения.
Второй вариант — когда на момент обработки события FD_READ во входном буфере находится только длина строки. В этом случае функция recv не будет помещать в очередь второе сообщение WM_SOCKETMESSAGE, т.к. данных в буфере после ее выполнения не останется, но и попытка выполнить этап чтения строки окажется бесполезной работой, т.к. строка еще не получена. В любом случае этап чтения строки будет выполнен только при обработке следующего сообщения WM_SOCKETMESSAGE, когда от клиента будут получены новые данные.
Получается, что при обоих вариантах попытка выполнить за один раз сразу два этапа не дает никаких преимуществ в быстродействии, но зато повышает вероятность ложных срабатываний события FD_READ в то время, когда сервер находится на этапе отправки данных клиенту. А ложные срабатывания на этом этапе вредны тем, что сервер принимает их за поступление данных, которое нужно запомнить, чтобы обработать после того, как ответ будет отправлен клиенту. В принципе, эти ложные срабатывания в итоге не приводят ни к чему плохому, кроме незначительного увеличения нагрузки на процессор, но раз от них нет пользы, и мы можем избавиться от них совсем небольшой ценой, лучше это сделать.