Клиент-Серверные приложения в Delphi[1]. Передача файлов



Актуальность вопроса.

В рунете я не видел статей, которые поднимали бы эту проблему, не считая локальных обсуждений на различных форумах. Зачем вообще нужно учить программу что-то передавать серверу? Ну допустим, вы закинули клиент на свой второй ПК. Программа снимает видео небольшими фрагментами или же фотографирует экран в определённое время, после чего ей нужно отослать эти фотографии на сервер, а иначе как вы их удалённо увидите? Или же нам нужно передать какой-либо файл с нашего сервера на наш клиент, непростая задача? Отталкиваясь от этого примера, мы сегодня научим сервер передавать изображения клиенту, в следующем уроке разберём, как клиенту передать что-то на сервер.

Самое главное, что сегодня мы будем работать со стандартными компонентами - TcpServer и TcpClient (вкладка Internet). Почему с ними? Потому что передавать буффер (или поток данных, или файл, если совсем грубо) через компоненты, рассмотренные в прошлом уроке - весьма муторно. Это мы рассмотрим в следующий раз.

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

Тем не менее напомню, что любой файл возможно передавать в структуре типа TStream (Поток). Что она из себя представляет? Это поток (а грубо говоря массив) байт, который, в зависимости от предназначения умеет выполнять те, или иные функции. Если мы работает с обычными бинарными файлами, то возможно использовать простую структуру TStream, если же работаем, например, с текстом - то можно использовать потомки TStream, заточенные под текст (TStringStream), если работаем с памятью ПК, то используем TMemoryStream и так далее. Вся структура потомков класса TStream представлена ниже:



Почитать о классах можете в Google :)

Таким образом получаем план работ:

1) Научить программу делать ScreenShot'ы экрана

2) Положить полученный ScreenShot в поток байт

3) Передать поток на сервер

На самом деле всё не так уж и сложно. Поехали.

1) Как делать ScreenShot экрана на делфи? Способов существует огромное количество, я написал следующий код:

  var bmp:TBitmap;  // Переменная типа TBitMap
begin
    bmp := TBitmap.Create; //Инициализируем
    bmp.Width := Screen.Width; //Ширину задаём шириной экрана
    bmp.Height := Screen.Height; //Высоту задаём высотой экрана
    BitBlt(bmp.Canvas.Handle, 0,0, Screen.Width, Screen.Height,   //Копируем содержимое экрана в переменную
           GetDC(0), 0,0,SRCCOPY);
    form1.Image1.Width := Screen.Width;
    form1.Image1.Height := Screen.Height;
    form1.Image1.Picture.Assign(bmp); // Выгружаем переменную в объект Image
    bmp.Free;
end; 


Проверка:



2) Теперь напишем код для процедуры Execute у компонента IdTCPServer, где мы будем получать команду от клиента и посылать ему ScreenShot:

var
    BM:TJPEGImage;
    cmd:string;
    Stream:TMemoryStream;
    bmp:TBitmap;
 
begin
 cmd:=AContext.Connection.IOHandler.ReadLn; //считываем команду
 memo1.Lines.Add('клиент '+AContext.Binding.PeerIP+' послал команду: '+cmd);
 if cmd='Give_Image' then //если клиент запросил картинку
 begin {тот самый код ScreenShot'а}
  BM:=TJPEGImage.Create;
  Stream:=TMemoryStream.Create;
 
    //скриншот
    bmp := TBitmap.Create;
    bmp.Width := Screen.Width;
    bmp.Height := Screen.Height;
    BitBlt(bmp.Canvas.Handle, 0,0, Screen.Width, Screen.Height,
           GetDC(0), 0,0,SRCCOPY);
    bm.Assign(bmp);
    //скриншот
  bm.SaveToStream(stream);
  Stream.Position:=0;
  //передаём картинку
  try
   AContext.Connection.IOHandler.Write(Stream,Stream.Size,true);
  finally
  AContext.Connection.Disconnect;
  BM.Free;
  Stream.Free;
  end;
 end;
 
end;


Форма Server'а содержит обычный компонент memo, который является журналом подключения:



Теперь сделаем форму для клиента, которая будет содержать лишь компонент Button, для запроса ScreenShot'а и компонент Image, для выгрузки полученного изображения:



Код кнопки "Получить":

var Stream:TMemoryStream;
     BM:TJPEGImage;
begin
 Stream:=TMemoryStream.Create;
 BM:=TJPEGImage.Create;
 
 form1.IdTCPClient1.Host:='127.0.0.1';
 form1.IdTCPClient1.Port:=7777;
 
 try
  form1.IdTCPClient1.Connect;//пытаемся соединится
  form1.IdTCPClient1.IOHandler.WriteLn('Give_Image'); //посылаем команду на сервер
 
  form1.IdTCPClient1.IOHandler.ReadStream(Stream); //считываем данные
  Stream.Position:=0;
  BM.CompressionQuality:=100;
  BM.LoadFromStream(Stream); //Выгружаем поток
  image1.Picture.Bitmap.Assign(BM); //Выгружаем переменную в Image
  finally
   form1.IdTCPClient1.Disconnect; //разорвали соединение
   BM.Free;
   Stream.Free
  end;
end;


Так же не забыли, что у Сервера нужно выставить свойство DefaultPort, поставив туда значение "7777", а так же свойство Bindings, указав там "0.0.0.0:7777". Ну и само собой Active ставим на true :)

Проверим?



Как видим всё удачно передалось. Что бы посмотреть полное изображение, нужно было растянуть форму :) Программу можно доработать, сделав её невидимой, или же наоборот, заставив клиента нам слать изображения на сервер. Экспериментируйте и успехов вам!
Теги:
TStream, нужно, сервер, возможно, использовать, работаем, после, определённое, время, клиента, передавать, грубо, говоря, поток, напомню, структуре, представляет, любой, Поток, менее
Добавлено: 24 Февраля 2015 22:03:17 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...