О чём не пишут в книгах по Delphi
Шрифт:
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
, когда от клиента будут получены новые данные.
Поделиться:
Популярные книги
Неудержимый. Книга VIII
8. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
6.00
рейтинг книги
Законы Рода. Том 6
6. Граф Берестьев
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга I
1. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Попаданка
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Возлюби болезнь свою
Научно-образовательная:
психология
7.71
рейтинг книги

Кодекс Крови. Книга III
3. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Ротмистр Гордеев 2
2. Ротмистр Гордеев
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Идеальный мир для Лекаря 5
5. Лекарь
Фантастика:
фэнтези
юмористическая фантастика
аниме
5.00
рейтинг книги
Адвокат Империи 3
3. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Жребий некроманта 3
3. Жребий некроманта
Фантастика:
боевая фантастика
5.56
рейтинг книги
Город драконов
1. Город драконов
Фантастика:
фэнтези
6.80
рейтинг книги
Убивать, чтобы жить
1. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Инквизитор Тьмы 2
2. Инквизитор Тьмы
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Беглец
1. Совсем не герой
Фантастика:
фэнтези
попаданцы
8.94