отсоединяется от сервера.
• Серверное приложение принимает строку от клиентского приложения и посылает ответ (также текстовый), после чего разрывает соединение. Плюс к этому ведется подсчет количества обслуженных клиентов и запоминается IP-адрес компьютера, с которого пришел последний запрос.
Реализация как серверного, так и клиентского приложений в нашем случае предельно проста. Проект серверного приложения Ha3biBaeTCflSimpleServer. Внешний вид формы сервера (во время работы приложения) представлен на рис. 11.3.
Рис. 11.3. Внешний вид простого сервера
Текстовое поле (Edit) с количеством обработанных запросов имеет имя txtCount, а текстовое поле с адресом последнего обслуженного компьютера названо txtFrom. Вся работа сервера заключается в обработке события Execute для компонента IdTCPServer, помещенного на форму (присоедините этот компонент к порту 12340 и установите значение свойства Active = True) (листинг 11.1).
Листинг 11.1. Реализация простого сервера
procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
strText: string;
begin
//Принимаем от клиента строку
strText := AThread.Connection.ReadLn;
//Отвечаем
AThread.Connection.WriteLn('Принял строку:' + strText);
//Обновим сведения на форме сервера (сервер многопоточный,
//поэтому используем синхронизацию)
section.Enter;
Inc(processed,1);
txtCount.Text := IntToStr(processed);
txtFrom.Text := AThread.Connection.Socket.Binding.PeerIP;
section.Leave;
//Закрываем соединение с пользователем
AThread.Connection.Disconnect;
end;
procedure TForm1.FormCreate (Sender: TObject);
begin
section := TCriticalSection.Create;
end;
При ответе клиенту сервер только повторяет принятую от него строку с добавлением текста Принял: в начало строки.
Анализируя листинг 11.1, можно заметить, что даже в рассматриваемом простейшем сервере пришлось применить синхронизацию при обновлении внешнего вида формы при помощи критической секции (необходимо дополнительно добавить имя модуля SyncObjs в секцию uses).
Теперь рассмотрим реализацию клиентской части (проект SimpleClient). Внешний вид клиентского приложения приведен на рис. 11.4.
Рис. 11.4. Внешний вид клиента
Естественно, что для работы клиентского приложения на форму помещен экземпляр компонента IdTCPQient (его имя – IdTCPClientl). Свойству Port этого компонента нужно присвоить значение 12 34 0. Текстовое поле (Edit) для ввода строки, подлежащей отправке не сервер, имеет HMfltxtMessage. Текстовое поле (Edit), в которое вводится имя или адрес сервера, названо txtServer. Поле со строками ответов (Memo) имеет имя txtResults. Вся работа клиентского приложения выполняется при нажатии кнопки Обработать. Текст соответствующего обработчика приведен в листинге 11.2.
Листинг 11.2. Реализация простого клиента
procedure TForm1.Button1Click(Sender: TObject);
begin
//Соединяемся с сервером и посылаем ему введенную строку
IdTCPClient1.Host := txtServer.Text;
IdTCPClient1.Connect;
IdTCPClient1.WriteLn (txtMessage.Text);
txtMessage.Text := '
//Ожидаем ответ и закрываем соединение
txtResults.Lines.Append (IdTCPClient1.ReadLn);
IdTCPClient1.Disconnect;
end;
Примечание
Для простоты в реализации клиентского приложения не производится обработка исключений, генерация которых возможна, например, при неправильном указании компьютера, на котором запущено серверное приложение. В более сложных примерах, с которыми вы познакомитесь далее в этой главе, обработка указанных исключений реализована.
Все, теперь можно запускать сервер и клиенты (на произвольном количестве компьютеров) и понаблюдать за результатами их работы. Только не забудьте запустить сервер до того, как будете обращаться к нему с помощью программы- клиента.
11.3. Слежение за компьютером по сети
Теперь рассмотрим более интересный пример использования сетевых компонентов IdTCPServer и IdTCPQient, который может быть полезен для людей, имеющих отношение к администрированию компьютеров сети.
Серверная программа предварительно запускается на наблюдаемом компьютере. В этом примере программа-сервер позволяет клиентской программе получать следующие сведения о компьютере, на котором она (программа-сервер) запущена:
• разрешение монитора;
• глубину цвета для монитора;
• полноразмерную копию экрана;
• копию экрана, уменьшенную (или увеличенную) до заданных размеров.
Для получения указанных сведений программа-клиент должна послать серверу следующие строковые значения:
• get_screen_width – для получения ширины и get_screen_height – для получения высоты экрана в пикселах;
• get_screen_colors – для получения значения установленной для монитора глубины цвета (бит на точку);
• get_screen – для получения полноразмерной копии экрана;
• get_screen: X, Y – для получения копии экрана, приведенной к размеру Хх Y.
Сначала рассмотрим реализацию сервера (проект SpyServer). Весь код, обеспечивающий работу сервера, помещен в модуле Unitl. pas формы Forml. Обработчик запросов клиентов – главная процедура для сервера – приводится в листинге 11.3.
Листинг 11.3.
Обработчик клиентских запросов
procedure TForm1.IdTCPServer1Execute (AThread: TIdPeerThread);
var
strText: string;
width, height, i: Integer;
dc: HDC;
begin
//Принимаем от клиента строку
strText := AThread.Connection.ReadLn;
//Определяем, что нужно выполнить
if (strText = 'get_screen_height') then
//Возвратим высоту экрана
Athread.Connection.WriteInteger (Screen.Height)
else if (strText = 'get_screen_width') then
//Возвратим ширину экрана
Athread.Connection.WriteInteger(Screen.Width)
else if (strText = 'get_screen_colors') then
begin
//Возвратим количество бит на точку
dc := GetDC (0);
Athread.Connection.WriteInteger(GetDeviceCaps(dc,
BITSPIXEL));
ReleaseDC(0, dc);
end
else if (strText = 'get_screen') then
//Возвратим полноразмерную копию экрана
SendScreen(Screen.Width, Screen.Height, AThread.Connection)
else begin //строка вида 'get_screen:x,y'
//Определим значения высоты и ширины,
//переданные пользователем
strText := Copy(strText, 12,Length(strText)-11);
i := Pos(',', strText); //Положение запятой
width := StrToInt(Copy(strText, 1, i-1));
height := StrToInt(Copy(strText, i+1, Length(strText)-i));
//Возвратим копию экрана
SendScreen(width, height, AThread.Connection);
end;
end;
Используемая в листинге 11.3 процедура SendScreen, отправляющая клиенту копию экрана, приведена в листинге 11.4.
Листинг 11.4.
Снятие копии экрана
//Процедура снимает копию экрана, приводит полученное
//изображение к заданному размеру и отправляет
//преобразованное изображение клиентской программе
procedure SendScreen(width: Integer; height: Integer;
Connection: TIdTCPServerConnection);
var
ScreenCopy: TCanvas;
gr: TBitmap;
stream: TMemoryStream;
rcDest, rcSource: TRect;
begin
rcDest := Rect(0, 0, width, height); //Конечный размер
//изображения
rcSource := Screen.DesktopRect; //Исходный размер
//изображения
//Создаем канву и присоединяем ее к контексту Рабочего стола
ScreenCopy := TCanvas.Create;
ScreenCopy.Handle := GetDC (0);
//Создаем объект для хранения копии экрана
//и копируем изображение
gr := TBitmap.Create;
gr.Height := height;
gr.Width := width;
gr.Canvas.CopyRect(rcDest, ScreenCopy,rcSource);
ReleaseDC(0, ScreenCopy.Handle);
//Сохраняем изображение в поток данных
stream := TMemoryStream.Create;
gr.SaveToStream(stream);
//Отправляем изображение клиенту
Connection.WriteStream (stream,True,True);
stream.Clear;
stream.Free;
gr.Free;
end;