Суббота, 18.05.2024
Королевство Delphi
Главное меню
Статьи
Наш опрос
Нравится раздел статьи?
Всего ответов: 68
Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0
Форма входа
Главная » Статьи » Сеть-интернет » Разные

Пишем программу для пересылки файлов через сокеты

Начинающие программисты (и я сам, когда начинал учить Delphi), задаются вопросом: как же передать файл через сокеты, если кроме этого файла через сокет передаётся ещё куча информации !? Вроде бы проблема не такая уж и сложная, но всё же не из лёгких... После долгих поисков в интернете, я так и не нашёл ни одной полезной статьи по этой теме. Вот я и решил исправить этот недостаток, и в этой статье я постараюсь помочь решить эту проблему...

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

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

И так приступим к написанию кода:
Начнём с сервера (он будет посылать файл):

Разместите на форму следующие компоненты: TServerSocket, TButton, TEdit, TProgressBar и TStatiusBar. Расположите их как показанно на рисунке.
Установите у компонента TServerSocket, порт (port): 1001.
Установите у компонента TStatusBar, переменную SimplePanel в true.
В строке , вводится название файла для передачи, кнопка TButton, используется для передачи файла.

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

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).
Не забудьте установить у компонента TStatusBar, переменную SimplePanel в true, чтоб было видно наш текст.
В одном TLabel'е выводится имя фала, в другой размер файла.
Должно получиться что-то похожее на это:

Объявляем переменные и оду процедуру. Запишите переменные именно в 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 // Если клиент не в режиме приёма файла, то...
 begin
 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;
end;

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

Ну вот и всё...
Клиент и сервер - готовы ! Сначала запустите сервер, а за тем клиента и попробуйте передать файлы, размером в несколько мегабайт :-) Я без проблем пересылал по сети файлы размером 10-12 Мб.

Удачи в программировании !

Получить ссылку на материал

Категория: Разные | Добавил: Барон (14.12.2011)
Просмотров: 1887 | Теги: сокет | Рейтинг: 5.0/1
[ Пожертвования для сайта ] [ Пожаловаться на материал ]

Если вам помог материал сайта кликните по оплаченной рекламе размещенной в центре

Поиск
Категории раздела
Web-приложения [6]
Почта [12]
Работа с HTTP [4]
Робота с XML [4]
Сервер [3]
Разные [50]
Королевство Delphi © 2010-2024
Яндекс цитирования