PDA

Просмотр полной версии : [Программа] Для пересылки файлов через сокеты.


.Master.
12.03.2014, 13:42
В этой теме я попробую доступно вам объяснить, как написать программу, которая сможет передавать файлы через сокеты (клиент и сервер), и кроме этого другие команды, например какое нибудь сообщение. Клиент у нас с вами будет принимать файлы или команды, а сервер - отсылать. Если наш клиент будет все подряд записывать в буфер, то кроме файла, в нём будут и команды, а нам нужно сделать так, чтоб файлы и команды не в коем случае не сливались. Так же мы должно учесть, если файл большой, то при пересылке, он разрежется на несколько пакетов, то есть файл перешлется не в одном пакете, а в нескольких, и событие OnClientRead будет вызываться несколько раз.

Чтобы мы смогли отделить команды от файла, сначала пошлём клиенту примерно такую строку: "file#file.txt#16", то есть: команда + разделитель + имя файла + разделитель + размер файла. При получении этой команды клиент перейдёт в режим приёма файла и всё подряд будет записывать в буфер, до тех пор пока размер файла не будет равен размеру принятых данных.

Начнем кодить...

Начнем с сервера, он будет посылать наш файл.

Кидаем на форму TServerSocket, TButton, TEdit, TProgressBar, TStatiusBar.

Устанавливаем у компонента TServerSocket, порт (port): 1001. У компонента TStatusBar, переменную SimplePanel в true. В строке, у нас вводится название файла для передачи, а кнопка ТБуттон - используется для передачи файла.

Сначала добавим буфер для файла в глобальные переменные:

.. var Form1: TForm1;

MS: TMemoryStream; // Буфер для файла


Теперь сделаем, чтоб при создании формы, открывался сокет:


procedure TForm1.FormCreate(Sender: TObject);

begin

ServerSocket1.Open; // Открываем сокет

end;

При завершении приложения, нужно не забыть закрыть сокет:

procedure TForm1.FormDestroy(Sender: TObject);

begin

ServerSocket1.Close; // Закрываем сокет

end;

При нажатии на кнопку - посылаем файл:

procedure TForm1.Button1Click(Sender: TObject); // Передаём файл

var

Size: integer;

P: ^Byte;

begin

MS := TMemoryStream.Create; // Создаём буфер для файла

MS.LoadFromFile(Edit1.Text); // Загружаем файл в буфер

// Посылаем информацию о файл (команда # название # размер)

ServerSocket1.Socket.Connections[0].SendText('file#'+Edit1.Text+'#'+IntToStr(MS.Size) +'#');

MS.Position := 0; // Переводим каретку в начало файла

P := MS.Memory; // Загружаем в переменную "P" файл

Size := ServerSocket1.Socket.Connections[0].SendBuf(P^, MS.Size); // Посылаем файл

// Выводим прогресс

ProgressBar1.Position := Size*100 div MS.Size;

StatusBar1.SimpleText := 'Отправлено '+IntToStr(Size)+' из '+IntToStr(MS.Size)+' байт'

end;

В событии OnClientRead, компонента TServerSocket, впишите следующий код:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;

Socket: TCustomWinSocket);

begin

if Socket.ReceiveText = 'end' then // Если клиент принял файл, то...

begin

StatusBar1.SimpleText := 'Клиент принял файл';

MS.Free; // Убиваем буфер

end;

end;

Это нам нужно для того, чтобы сервер убил буфер, только после того, как клиент примет файл. Если убить буфер, сразу после передачи файла, то клиент не успеет принять весь файл. Как только клиент примет файл он пошлёт серверу команду "end", что значит файл принят, и сервер убьёт буфер.


Теперь попробуем сделать так, чтобы наш сервер выводил немного информации о соединении: На событие OnClientConnect, компонента TServerSocket впишите следующий код:

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

StatusBar1.SimpleText := 'Соединение установлено';

end;

На событие OnClientDisconnect вписываем:

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

StatusBar1.SimpleText := 'Соединение не установлено';

end;

Ок, сервер мы сделали. Давайте теперь перейдем к клиенту (он принимает файл) с ним как правило всегда больше возни.

Кидаем на форму: TClientSocket, две метки TLabel, TProgressBar и TStatusBar. У компонента TClientSocket устанавливаем порт (port): 1001 (как у сервера), а переменную адрес (address): 127.0.0.1 (ваш IP). Объявляем переменную и одну процедуру. Переменные записываем в private, иначе ничего у нас с вами не будет робить.


procedure Writing(Text: string); // Процедура записи в данных в буфер

private

{ Private declarations }

Name: string; // Имя файла

Size: integer; // Размер файла

Receive: boolean; // Режим клиента

MS: TMemoryStream; // Буфер для файла

На событие создания формы, мы соединяемся с сервером и ждём передачи файла:

procedure TForm1.FormCreate(Sender: TObject);

begin

ClientSocket1.Open; // Открываем сокет

Receive := false; // Режим клиента - приём команд

end;

По завершению приложения - закрываем сокет:

procedure TForm1.FormDestroy(Sender: TObject);

begin

ClientSocket1.Close; // Закрываем сокет

end;

Так же как и у сервера, делаем чтобы клиент выдавал информацию о соединении:

procedure TForm1.ClientSocket1Connect(Sender: TObject;

Socket: TCustomWinSocket);

begin

StatusBar1.SimpleText := 'Соединение установлено';

end;


procedure TForm1.ClientSocket1Disconnect(Sender: TObject;

Socket: TCustomWinSocket);

begin

StatusBar1.SimpleText := 'Соединение не установлено';

end;

Теперь вписываем код в процедуру Writing. Процедура эта нужна нам для того, чтобы принятые данные записывать в файл. Код таков:

procedure TForm1.Writing(Text: string);

begin

if MS.Size < Size then // Если принято байт меньше размера файла, то...

MS.Write(Text[1], Length(Text)); // Записываем в буфер

// Выводим прогресс закачки файла

ProgressBar1.Position := MS.Size*100 div Size;

StatusBar1.SimpleText := 'Принято '+IntToStr(MS.Size)+' из '+IntToStr(Size);

if MS.Size = Size then // Если файл принят, то...

begin

Receive := false; // Переводим клиента в нормальный режим

MS.Position := 0; // Переводим каретку в начало буфера

MS.SaveToFile(Name); // Сохраняем файл

ClientSocket1.Socket.SendText('end'); // Посылаем команду "end", то есть файл принят

MS.Free; // Убиваем буфер

StatusBar1.SimpleText := 'Файл принят';

end;

end;

Теперь на событие OnClientRead компонента TClientSocket, вписываем следующий код:

procedure TForm1.ClientSocket1Read(Sender: TObject;

Socket: TCustomWinSocket);

var

Rtext: string; // Принятый текст

begin

Rtext := Socket.ReceiveText;

if Receive then // Если клиент в режиме приёма файла, то...

Writing(RText) // Записываем данные в буфер

else // Если клиент не в режиме приёма файла, то...

if Copy(Rtext, 0, Pos('#', Rtext) -1) = 'file' then // Если это файл, то...

begin MS := TMemoryStream.Create; // Создаём буфер для файла

Delete(Rtext, 1, Pos('#', Rtext)); // Определяем имя файла

Name := Copy(Rtext, 0, Pos('#', Rtext) -1); // Определяем имя файла

Delete(Rtext, 1, Pos('#', Rtext)); // Определяем размер файла

Size := StrToInt(Copy(Rtext, 0, Pos('#', Rtext) -1)); // Определяем размер файла

Delete(Rtext, 1, Pos('#', Rtext)); // Удаляем последний разделитель

Label1.Caption := 'Размер файла: '+IntToStr(Size)+' байт'; // Выводим размер файла

Label2.Caption := 'Имя файла: '+Name; // Выводим имя файла

Receive := true; // Переводим сервер в режим приёма файла

Writing(RText); // Записываем данные в буфер

end;

end;

Если файл большой, и событие OnClientRead будет вызываться ни один раз, а несколько, то если клиент в режиме приёма файла, он будет записывать данные в буфер, если же нет, то клиент определит принятую команду, и если это файл, то перейдёт в режим приёма файла. Вот и все Клиент и сервер - готовы. Компилируем нашу программу и запускаем сервера, а потом клиенты.

Всем спасибо за внимание.