О чём не пишут в книгах по 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,
Поделиться:
Популярные книги
70 Рублей
1. 70 Рублей
Фантастика:
фэнтези
боевая фантастика
попаданцы
постапокалипсис
6.00
рейтинг книги
Жатва душ. Остров мертвых
Фантастика:
боевая фантастика
рпг
5.20
рейтинг книги
Сердце Дракона. Том 9
9. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.69
рейтинг книги
Как я строил магическую империю 6
6. Как я строил магическую империю
Фантастика:
попаданцы
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Леди для короля. Оборотная сторона короны
3. Королевская охота
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Кодекс Крови. Книга VI
6. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Газлайтер. Том 4
4. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
АллатРа
Научно-образовательная:
психология
история
философия
обществознание
физика
6.25
рейтинг книги
Газлайтер. Том 10
10. История Телепата
Фантастика:
боевая фантастика
5.00
рейтинг книги
Дорога к счастью
Любовные романы:
любовно-фантастические романы
6.11
рейтинг книги
Газлайтер. Том 8
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Камень Книга седьмая
7. Камень
Фантастика:
фэнтези
боевая фантастика
6.22
рейтинг книги
Николай I Освободитель. Книга 2
2. Николай I
Фантастика:
героическая фантастика
альтернативная история
5.00
рейтинг книги
Секретарша генерального
Любовные романы:
современные любовные романы
эро литература
короткие любовные романы
8.46