О чём не пишут в книгах по Delphi
Шрифт:
ClientSocket: TSocket;
ClientAddr: string;
MsgSize: Integer;
Msg: string;
Offset: Integer;
BytesLeft: Integer;
Overlapped: TWSAOverlapped;
end;
Основное отличие этого варианта типа
TConnection
от того, что применялся ранее в примерах NonBlockingServer
и AsyncSelectServer
(см. разд. 2.1.16
Phase
, которое хранит этап взаимодействия с клиентом. Разумеется, в программе OverlappedServer
взаимодействие с клиентом также разбивается на три этапа, но реализуется другой способ для того, чтобы различать этапы — для каждого этапа создается своя процедура завершения. Примечание
Использование одной процедуры завершения для всех трех этапов и распознавание в ней этапов с помощью поля
Phase
в случае перекрытого ввода-вывода также возможно. Рекомендуем написать такой вариант сервера в качестве самостоятельного упражнения. Поле
Overlapped
содержит структуру TWSAOverlapped
, которой программа непосредственно не пользуется, она только передает указатель на эту структуру в функции WSARecv
и WSASend
. Напомним, что одновременно может выполняться несколько операций перекрытого ввода-вывода, но у каждой из этих операций должен быть свой экземпляр TWSAOverlapped
. Гак как в нашем случае с одним клиентом в каждый момент времени может выполняться не более одной операции, мы создаем по одному экземпляру TWSAOverlapped
на каждого клиента. Функция для перекрытого подключения клиентов существует — это
AcceptEx
, с которой мы познакомимся в разд. 2.2.12. Но она неудобна при работе совместно с WSARecv
и WSASend
, особенно в таком строго типизированном языке, как Delphi. Поэтому подключение клиентов мы будем отслеживать с помощью уже опробованной технологии асинхронных сокетов на сообщениях. Код запуска сервера OverlappedServer выглядит идентично коду запуска AsyncSelectServer (см. листинг 2.30): точно так же создается сокет, ставится в режим прослушивания, а затем его событие FD_ACCEPT
привязывается к сообщению WM_ACCEPTMESSAGE
. Сам обработчик
WM_ACCEPTMESSAGE
выглядит теперь следующим образом (листинг 2.77). Листинг 2.77. Обработчик сообщения WM_
ACCEPTMESSAGE
procedure TServerForm.WMAcceptMessage(var Msg: TWMSocketMessage);
var
NewConnection: PConnection;
// Сокет, который создается для вновь подключившегося клиента
ClientSocket: TSocket;
// Адрес подключившегося клиента
ClientAddr: TSockAddr;
// Длина адреса
AddrLen: Integer;
// Аргумент для перевода сокета в неблокирующий режим
Arg: u_long;
// Буфер для операции перекрытого чтения
Buf: TWSABuf;
NumBytes, Flags: DWORD;
begin
//
Страхуемся от "тупой" ошибки
if Msg.Socket <> FServerSocket then
raise ESocketError.Create(
'Внутренняя ошибка сервера - неверный серверный сокет');
// Обрабатываем ошибку на сокете, если она есть
if Msg.SockError <> 0 then
begin
MessageDlg('Ошибка при подключении клиента:'#13#10 +
GetErrorString(Msg.SockError) +
#13#10'Сервер будет ocтановлен', mtError, [mbOK], 0);
ClearConnections;
closesocket(FServerSocket);
OnStopServer;
Exit;
end;
// Страхуемся от ещё одной "тупой" ошибки
if Msg.SockEvent <> FD_ACCEPT then
raise ESocketError.Create(
'Внутренняя ошибка сервера - неверное событие на сокете');
AddrLen := SizeOf(TSockAddr);
ClientSocket := accept(FServerSocket, @ClientAddr, @AddrLen);
if ClientSocket = INVALID_SOCKET then
begin
// Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает
// что на данный момент подключений нет, а вообще все а порядке,
// поэтому ошибку WSAEWOULDBLOCK мы просто игнорируем. Прочие же
// ошибки могут произойти только в случае серьезных проблем,
// которые требуют остановки сервера.
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
MessageDlg('Ошибка при подключении клиента:'#13#10 +
GetErrorString + #13#10'Сервер будет остановлен',
mtError, [mbOK], 0);
ClearConnections;
closesocket(FServerSocket);
OnStopServer;
end;
end
else
begin
// Новый сокет наследует свойства слушающего сокета.
// В частности, он работает в асинхронном режиме,
// и его событие FD_ACCEPT связано с сообщением WM_ACCEPTMESSAGE.
// Так как нам это совершенно не нужно, отменяем асинхронный
// режим и делаем сокет блокирующим.
if WSAAsyncSelect(ClientSocket, Handle, 0, 0) = SOCKET_ERROR then
begin
MessageDlg('Ошибка при отмене асинхронного режима ' +
'подключившегося сокета:'#13#10 + GetErrorString,
Поделиться:
Популярные книги
Мастер Разума V
5. Мастер Разума
Фантастика:
городское фэнтези
попаданцы
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности
1. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Месть бывшему. Замуж за босса
3. Власть. Страсть. Любовь
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Треск штанов
6. Сын Петра
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Мужчина не моей мечты
1. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.30
рейтинг книги
Имя нам Легион. Том 5
5. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Лорд Системы 11
11. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Последний реанорец. Том III
2. Высшая Речь
Фантастика:
фэнтези
попаданцы
5.25
рейтинг книги
Идеальный мир для Лекаря 21
21. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Убивать чтобы жить 6
6. УЧЖ
Фантастика:
боевая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
Новый Рал 3
3. Рал!
Фантастика:
попаданцы
5.88
рейтинг книги
Мимик нового Мира 7
6. Мимик!
Фантастика:
юмористическое фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Уязвимость
Любовные романы:
современные любовные романы
7.44
рейтинг книги
Сильнейший ученик. Том 2
2. Пробуждение крови
Фантастика:
фэнтези
попаданцы
аниме
5.00