Как можно увидеть, даже самая сложная операция рассматриваемого сервера копирование изображения – реализуется довольно просто благодаря наличию такого стандартного класса, как TMemoryStream.
При реализации сервера использован таймер. Он применен для скрытия формы сервера сразу при запуске приложения (не забудьте установить значения его свойств Enabled = True и Interval = 50). Компонент IdTCPServer (с именем IdTCPServerl) в этом примере присоединен к порту 12341 (не забудьте также установить свойство Active = True).
Теперь о реализации клиентского приложения (проект SpyClient). Внешний вид формы (Forml) клиента во время работы приводится на рис. 11.5 (видно, что пользователь наблюдаемого компьютера только что проиграл в игру Сапер ).
Рис. 11.5. Внешний вид клиента слежения
Описания, имена и значения настроенных вручную свойств самых важных компонентов формы клиента приведены в табл. 11.1. Таблица 11.1. Основные компоненты формы клиента слежения и их свойства
Листинг 11.5. Соединение с сервером
procedure TForm1.cmbConnectClick(Sender: TObject);
begin
if (cmbConnect.Caption = 'Подключиться') then
begin
if (txtServer.Text = '') then
//Не введено имя сервера
MessageDlg('Введите имя машины-сервера в текстовое поле',
mtInformation, [mbOK], 0)
else begin
//Подключаемся к серверу
IdTCPClient1.Host := txtServer.Text;
try
IdTCPClient1.Connect;
except
MessageDlg('Не удается соединиться с указанным сервером',
mtError, [mbOK], 0);
Exit;
end;
end
end
else begin
//Отключаемся от сервера
IdTCPClient1.Disconnect;
end;
end;
Если соединение с сервером произошло успешно, то выполняется обработчик TForml. IdTCPClientlConnected, подготавливающий приложение-клиент к периодическим запросам данных с сервера (листинг 11.6).
Листинг 11.6.
Действия, выполняемые при соединении с сервером
procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
txtServer.Enabled := False;
cmbConnect.Caption := 'Отключиться
//Начинаем периодически запрашивать данные с сервера
Timer1.Enabled := True;
//Выполним первый запрос, не дожидаясь срабатывания таймера
Timer1Timer (Nil);
end;
При отсоединении от сервера также выполняются действия, прекращающие периодические запросы данных и переводящие клиент в состояние ожидания подключения (первоначальное состояние программы) (листинг 11.7).
Листинг 11.7.
Действия при отсоединении от сервера
procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
txtServer.Enabled := True;
cmbConnect.Caption := 'Подключиться
Timer1.Enabled := False;
end;
Самой сложной частью клиентского приложения является обработка данных, присылаемых сервером. Клиентское приложение запрашивает данные по таймеру и обрабатывает полученные данные так, как показано в листинге 11.8.
Листинг 11.8.
Запрос и обработка данных, полученных с сервера
procedure TForm1.Timer1Timer(Sender: TObject);
var
stream: TMemoryStream;
begin
//Запрашиваем у сервера данные о наблюдаемом компьютере
with (IdTCPClient1) do
begin
//…разрешение
WriteLn ('get_screen_width');
WriteLn('get_screen_height');
lblResolution.Caption := IntToStr (ReadInteger) + 'x' +
IntToStr(ReadInteger);
//…глубина цвета
WriteLn ('get_screen_colors');
lblColors.Caption := IntToStr(ReadInteger);
//…копия экрана
//.....первый вариант – копирование экрана без сжатия
WriteLn('get_screen ');
//.....второй вариант – сжатие на стороне сервера
WriteLn('get_screen:' + IntToStr (imgScreen.Width) + ',' +
IntToStr(imgScreen.Height));
//....получаем данные
stream := TMemoryStream.Create;
ReadStream(stream);
stream.Position := 0;
//....формируем изображение
imgScreen.Picture.Bitmap.LoadFromStream (stream);
stream.Clear;
stream.Free;
end;
end;
В тексте листинга 11.8 создано большое количество комментариев, поэтому дополнительно пояснять его нет смысла. Остановимся лишь на том, зачем в процедуре TForml.TimerlTimer предусмотрено два варианта получения изображения с сервера.
Все дело в том, что сжатие (в нашем примере разрешение экрана наблюдаемого компьютера больше размера компонента imgScreen) на стороне сервера требует от компьютера, на котором запущено серверное приложение, большего процессорного времени на снятие копии экрана. Это снижает нагрузку на сеть при передаче изображения, а также экономит ресурсы компьютера-клиента. Но качество сжатого изображения в этом случае получается несколько хуже, чем когда мы предоставляем компоненту Image возможность масштабировать изображение самостоятельно.
Если же не использовать сжатие изображения на сервере, возрастает нагрузка на сеть при передаче полноразмерной копии экрана, а вся работа по сжатию изображения возлагается на компонент imgScreen (то есть дополнительно тратится процессорное время на компьютере клиента). При большом разрешении экрана наблюдаемого компьютера (или при наблюдении сразу за несколькими компьютерами) машина клиента, если она недостаточно мощная, может начать весьма ощутимо «тормозить». Качество сжатого изображения при этом получается более высоким.
В качестве более-менее эффективного решения можно предложить использовать большие промежутки времени между запросами данных с сервера слежения с масштабированием изображения на серверной стороне (если только машина сервера не является очень маломощной).
11.4. Многопользовательский разговорник
В завершение знакомства с компонентамиIdTCPCLient и IdTCPServer для организации сетевого взаимодействия рассмотрим создание полноценного клиент- серверного приложения – многопользовательского разговорника. Как можно догадаться из названия, это приложение будет позволять обмениваться сообщениями большому количеству пользователей (наподобие чата).
Поскольку этот пример будет несколько сложнее (в плане организации сетевого взаимодействия) предыдущих примеров главы, то рассмотрим подробно основные этапы его проектирования, разработки и реализации (начиная с требований и поведения клиента и сервера и заканчивая нюансами реализации приложений).
Требования к клиентскому и серверному приложениям
Пользователи при работе с клиентскими приложениями должны иметь следующие возможности:
• видеть полный текст разговора с момента их подключения к серверу;
• отсылать сообщения как всем, так и только определенным пользователям;
• видеть список пользователей, участвующих в разговоре (при этом список должен автоматически обновляться при отключении или присоединении новых пользователей);
• получать уведомления об отключении или присоединении новых пользователей (прямо в тексте разговора).
Серверное приложение, кроме управления подключением, отключением пользователей, а также доставки сообщений, должно обеспечивать протоколирование событий (подключение, отключение пользователей, от кого и кому послано то или иное сообщение).
При реализации серверного приложения нужно преодолеть некоторые сложности, связанные с тем, что к серверу будут постоянно подключены сразу несколько пользователей, причем информация о каждом пользователе будет постоянно храниться и использоваться сервером. Нужно также обеспечить надежную работу клиентского, а главное – серверного приложения при проблемах, связанных с неисправностями сети.
И, наконец, нужно обеспечить автоматическую рассылку клиентским приложениям следующей информации (клиенты эту информацию специально с сервера не запрашивают):
• текста сообщений;
• уведомлений о присоединении или отсоединении пользователей.
Формат сообщений клиента и сервера
Клиент и сервер обмениваются только текстовыми сообщениями (не путать с сообщениями, которыми обмениваются пользователи в ходе разговора). Строка любого сообщения состоит из двух частей: префикса и текста сообщения. Префикс отделяется от текста сообщения символом: (двоеточие). По префиксу можно