Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

end;

Данный метод вызывается в обработчике нажатия кнопки Остановить и при завершении приложения. Сервер можно многократно останавливать и запуска вновь, не завершая приложение.

Чтобы увидеть все возможности сервера, потребуется новый клиент. На компакт-диске он называется EventSelectClient, но "EventSelect" в данном случае означает только то, что клиент является парным к серверу EventSelectServer. Сам клиент функцию

WSAEventSelect
не использует, поскольку она неудобна, когда нужно работать только с одним сокетом. Поэтому клиент работает в асинхронном режиме, основанном на сообщениях, т.е. посредством функции
WSAAsyncSelect
.

Клиент

может получать от сервера сообщения двух видов: те. которые сервер посылает в ответ на запросы клиента, и те, которые он посылает по собственной инициативе. Но различить эти сообщения клиент не может: например, если клиент отправляет запрос серверу, а потом получает от него сообщение, он не может определить, то ли это сервер ответил на его запрос, то ли именно в этот момент сервер сам отправил клиенту свое сообщение. Соответственно, сообщения обоих типов читает один и тот же код.

Примечание

В принципе, протокол мог бы быть определен таким образом, что ответы на запросы клиента и сообщения, посылаемые сервером по собственной инициативе, имели бы разный формат, по которому их можно было бы различить и читать по-разному. Но даже при этом форматы нельзя различить, пока сообщение не будет прочитано хотя бы частично, так что начало чтения будет выполняться единообразно в любом случае.

Подключение клиента к серверу выполняется точно так же, как в листинге 2.16, за исключением того, что после выполнения функции

connect
сокет переводится в асинхронный режим, и его события
FD_READ
и
FD_CLOSE
связываются с сообщением
WM_SOCKETMESSAGE
. Обработчик этого сообщения приведен в листинге 2.66.

Листинг 2.66. Получение данных клиентом

procedure TESClientForm.WMSocketMessage(var Msg: TWMSocketMessage);

const

 // Размер буфера для получения данных

 RecvBufSize = 4096;

var

 // Буфер для получения данных

 RecvBuf: array[0..RecvBufSize - 1] of Byte;

 RecvRes: Integer;

 P: Integer;

begin

 // Защита от "тупой" ошибки

 if Msg.Socket <> FSocket then

 begin

MessageDlg('Внутренняя ошибка программы — неверный сокет',

mtError, [mbOK], 0);

Exit;

 end;

 if Msg.SockError <> 0 then

 begin

MessageDlg('Ошибка при взаимодействии с сервером'#13#10 +

GetErrorString(Msg.SockError), mtError, [mbOK], 0);

OnDisconnect;

Exit;

 end;

 case Msg.SockEvent of

 FD_READ:

 // Получено сообщение от сервера

 begin

// Читаем столько, сколько можем

RecvRes := recv(FSocket, RecvBuf, RecvBufSize, 0);

if RecvRes > 0 then

begin

// Увеличиваем строку на размер прочитанных данных

P := Length(FRecvStr);

SetLength(FRecvStr, P + RecvRes);

//
Копируем в строку полученные данные

Move(RecvBuf, FRecvStr[Р + 1], RecvRes);

// В строке может оказаться несколько строк от сервера,

// причем последняя может прийти не целиком.

// Ищем в строке символы #0, которые, согласно протоколу,

// являются разделителями строк.

P := Pos(#0, FRecvStr));

while P > 0 do

begin

AddMessageToRecvMemo('Сообщение от сервера: ' +

Copy(FRecvStr, 1, P - 1));

// Удаляем из строкового буфера выведенную строку

Delete(FRecvStr, 1, P);

P := Pos(#0, FRecvStr);

end;

end

else if RecvRes = 0 then

begin

MessageDlg('Сервер закрыл соединение'#13#10 +

GetErrorString, mtError, [mbOK], 0);

OnDisconnect;

end

else

begin

if WSAGetLastError <> WSAEWOULDBLOCK then

begin

MessageDlg('Ошибка при получении данных от клиента'#13#10 +

GetErrorString, mtError, [mbOK], 0);

OnDisconnect;

end;

end;

 end;

 FD_CLOSE: begin

MessageDlg('Сервер закрыл соединение', mtError, [mbOK], 0);

shutdown(FSocket, SD_BOTH);

OnDisconnect;

 end;

 else begin

MessageDlg('Внутренняя ошибка программы — неизвестное событие ' +

IntToStr(Msg.SockEvent), mtError, [mbOK], 0);

OnDisconnect;

 end;

 end;

end;

Здесь мы используем новый способ чтения данных. Он во многом похож на тот, который применен в сервере. Функция

recv
вызывается один раз за один вызов обработчика значений и передаст данные в буфер фиксированного размера
RecvBuf
. Затем в буфере ищутся границы отдельных строк (символы
#0
), строки, полученные целиком, выводятся. Если строка получена частично (а такое может случиться не только из-за того, что она передана по частям, но и из-за того, что в буфере просто не хватило место для приема ее целиком), её начало следует сохранить в отдельном буфере, чтобы добавить к тому, что будет прочитано при следующем событии
FD_READ
. Этот буфер реализуется полем
FRecvStr
типа
string
. После чтения к содержимому этой строки добавляется содержимое буфера
RecvBuf
, а затем из строки выделяются все подстроки, заканчивающиеся на
#0
. То, что остается в строке
FRecvStr
после этого, — это начало строки, прочитанной частично. Оно будет учтено при обработке следующего события
FD_READ
.

Поделиться:
Популярные книги

Право налево

Зика Натаэль
Любовные романы:
современные любовные романы
8.38
рейтинг книги
Право налево

Третий. Том 2

INDIGO
2. Отпуск
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
Третий. Том 2

Гром над Империей. Часть 2

Машуков Тимур
6. Гром над миром
Фантастика:
фэнтези
попаданцы
5.25
рейтинг книги
Гром над Империей. Часть 2

Сама себе хозяйка

Красовская Марианна
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Сама себе хозяйка

Бальмануг. Невеста

Лашина Полина
5. Мир Десяти
Фантастика:
юмористическое фэнтези
5.00
рейтинг книги
Бальмануг. Невеста

Аромат невинности

Вудворт Франциска
Любовные романы:
любовно-фантастические романы
эро литература
9.23
рейтинг книги
Аромат невинности

Чемпион

Демиров Леонид
3. Мания крафта
Фантастика:
фэнтези
рпг
5.38
рейтинг книги
Чемпион

Эксперимент

Юнина Наталья
Любовные романы:
современные любовные романы
4.00
рейтинг книги
Эксперимент

Небо для Беса

Рам Янка
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Небо для Беса

Идеальный мир для Социопата 7

Сапфир Олег
7. Социопат
Фантастика:
боевая фантастика
6.22
рейтинг книги
Идеальный мир для Социопата 7

Я не князь. Книга XIII

Дрейк Сириус
13. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я не князь. Книга XIII

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Романов. Том 1 и Том 2

Кощеев Владимир
1. Романов
Фантастика:
фэнтези
попаданцы
альтернативная история
5.25
рейтинг книги
Романов. Том 1 и Том 2

Ваше Сиятельство 4т

Моури Эрли
4. Ваше Сиятельство
Любовные романы:
эро литература
5.00
рейтинг книги
Ваше Сиятельство 4т