Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 04.05.2019, 21:16   #1
niockasd
Пользователь
 
Регистрация: 01.12.2015
Сообщений: 82
По умолчанию передача больших файлов по сети

Всем привет, не могу понять что происходит.

На основе примеров из сети организовал у себя передачу файлов через сокеты.

Файлы до 500 МБ передаются отлично, без сбоев.
Файл размеров в 700 МБ передается на клиент (служба виндовс), клиент распухает в 639 МБ озу и все, дальше дело не идет.

Отправляю так:
Код:
Procedure TransferFiles(FilenameLocl: String ; FileNameRemote: String );
var
 Size: int64;
 P: ^Byte;
 MS: TMemoryStream;
begin
       TRY
           MS := TMemoryStream.Create; // Создаём буфер для файла
            TRY
              MS.LoadFromFile(FilenameLocl); // Загружаем файл в буфер
            EXCEPT
             showmessage('Ошибка загрузки '+FilenameLocl);
            END;
            // Посылаем информацию о файл (команда # название # размер)
            TCPTransferToClientForm.FileTransferSocket.Socket.Connections[0].SendText('file#'+FileNameRemote+'#'+IntToStr(MS.Size)+'#');
            MS.Position := 0;
            P := MS.Memory;

            Size := TCPTransferToClientForm.FileTransferSocket.Socket.Connections[0].SendBuf(P^, MS.Size); // Посылаем файл
              // Выводим прогресс
            TCPTransferToClientForm.ProgressBar1.Position :=   Size;
            TCPTransferToClientForm.StatusBar1.Panels[0].Text := 'Отправлено '+IntToStr(Size)+' из '+IntToStr(MS.Size)+' байт';
       FINALLY
          ms.Free;
       END;
end;
Принимаю так
Код:
var
FileTrasfer_Name: string; // Имя файла
FileTrasfer_Size: int64; // Размер файла
FileTrasfer_Receive: boolean; // Режим клиента
FileTrasfer_MS: TMemoryStream; // Буфер для файла
...




procedure TService.FileTransferSocketCleintRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Rtext: ansistring; // Принятый текст
begin
  Rtext := Socket.ReceiveText;
  if FileTrasfer_Receive then // Если клиент в режиме приёма файла, то...
    Writing(RText) // Записываем данные в буфер
  else // Если клиент не в режиме приёма файла, то...
  begin

    if Copy(Rtext, 0, Pos('#', Rtext) -1) = 'file' then // Если это файл, то...
    begin
      FileTrasfer_MS := TMemoryStream.Create; // Создаём буфер для файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем имя файла

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

      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем размер файла
      FileTrasfer_Size := StrToInt(Copy(Rtext, 0, Pos('#', Rtext) -1)); // Определяем размер файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Удаляем последний разделитель

      FileTrasfer_Receive := true; // Переводим сервер в режим приёма файла
      Writing(RText); // Записываем данные в буфер
    end;
  end;
end;




procedure Writing(Text: AnsiString);
begin
  if FileTrasfer_MS.Size < FileTrasfer_Size then // Если принято байт меньше размера файла, то...
    FileTrasfer_MS.Write(Text[1], Length(Text)); // Записываем в буфер

  if FileTrasfer_MS.Size = FileTrasfer_Size then // Если файл принят, то...
  begin
    FileTrasfer_Receive := false; // Переводим клиента в нормальный режим
    FileTrasfer_MS.Position := 0; // Переводим каретку в начало буфера

    FileTrasfer_MS.SaveToFile(FileTrasfer_Name); // Сохраняем файл
    Service.FileTransferSocketCleint.Socket.SendText('end'); // Посылаем команду "end", то есть файл принят
    FileTrasfer_MS.Free; // Убиваем буфер
  end;

  //что то пошло не так принял больше чем ждал
  if FileTrasfer_MS.Size >= FileTrasfer_Size then // Если файл принят, то...
  begin
    Service.FileTransferSocketCleint.Socket.SendText('end'); // Посылаем команду "end", то есть файл принят
    FileTrasfer_MS.Free; // Убиваем буфер
  end;
end;

целочисленные переменные вроде везде int64... В чем еще проблема может быть?
Embarcadero® Delphi 10.3 Version 26.0.32429.4364

Последний раз редактировалось niockasd; 04.05.2019 в 21:47.
niockasd вне форума Ответить с цитированием
Старый 04.05.2019, 21:49   #2
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Чтобы клиент не распухал нужно сразу на диск писать файл лучше использовать File через BlockWrite
Либо кусками через TMemoryStream принял почистил перешел в конец файла и так далее но это геморойно

примерно так на вашем примере
Код:
   
FFile: File;
............

function OpenFile(out AFile: File; const AFileName : UTF8String): longbool;
Var
 i: integer;
begin
Result:= false;
if (AFileName <> '') then
begin
 		{$I-}
		AssignFile ( AFile, AFileName );
		FileMode := fmOpenReadWrite;
		Reset ( AFile, 1 );
		{$I+}
		i := IOResult;
		If i = 5 Then
		Begin
			{$I-}
			AssignFile ( AFile, AFileName );
			FileMode := fmOpenWrite;
			rewrite ( AFile, 1 );
		 	{$I+}
			i := IOResult;
	 	End;
if i <> 0 then
 CloseFile(AFile)
 else
 Result:= true;
 end;
 end;

procedure TService.FileTransferSocketCleintRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Rtext: ansistring; // Принятый текст
begin
  Rtext := Socket.ReceiveText;
  if FileTrasfer_Receive then // Если клиент в режиме приёма файла, то...
    Writing(RText) // Записываем данные в буфер
  else // Если клиент не в режиме приёма файла, то...
  begin

    if Copy(Rtext, 0, Pos('#', Rtext) -1) = 'file' then // Если это файл, то...
    begin
      FileTrasfer_MS := TMemoryStream.Create; // Создаём буфер для файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем имя файла

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

      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем размер файла
      FileTrasfer_Size := StrToInt(Copy(Rtext, 0, Pos('#', Rtext) -1)); // Определяем размер файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Удаляем последний разделитель

      FileTrasfer_Receive := OpenFile(FFile, FileTrasfer_Name); // Переводим сервер в режим приёма файла
      Writing(RText); // Записываем данные в буфер
    end;
  end;
end;

procedure Writing(Text: AnsiString);
begin
  if FileTrasfer_MS.Size < FileTrasfer_Size then // Если принято байт меньше размера файла, то...
     BlockWrite(FFile, Pointer(Text)^, Length(Text)); // Записываем буфер в файл

  if FileTrasfer_MS.Size = FileTrasfer_Size then  // Если файл принят, то...
  begin
    FileTrasfer_Receive := false; // Переводим клиента в нормальный режим

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

  //что то пошло не так принял больше чем ждал
  if FileTrasfer_MS.Size >= FileTrasfer_Size then // Если файл принят, то...
  begin
    Service.FileTransferSocketCleint.Socket.SendText('end'); // Посылаем команду "end", то есть файл принят
    CloseFile(FFile); // закрываем файл
  end;
end;

Последний раз редактировалось Aliens_wolfs; 04.05.2019 в 22:59.
Aliens_wolfs вне форума Ответить с цитированием
Старый 04.05.2019, 22:00   #3
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

В ОС память фрагментирована. На границе 1400 мб весит виндовая библиотека. Поэтому ваша программа не может выделить память больше чем 1400 Мб. А когда скачивается поток возникает необходимость релоцировать память под поток TMemoryStream. Что такое релокация? Это изменение размера выделенной памяти. Т.е. для потока выделено 700 мб вы записали все 700 мб и вам нужно записать следующий байт. Системная библиотека выделяет на 1 мб больше - 701 мб. Затем копирует 700 мб от старого участка и освобождает старую память и до записывает следующий байт. Но при 700 мб занятых ваша программа не может дополнительно выделить 701 мб. Так как 700+701=1401 мб что больше 1400 мб, то системная библиотека возвращает ошибку Out of Memory. А пользовательское пространство для 32 битных программ ограниченно 2 Гб. Остальные 2Гб отведены для ядра ОС.

У вас два выхода:
1) Компилировать под платформу 64 бита
2) Передавать по частям и писать в файл принятые части.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 04.05.2019, 22:02   #4
niockasd
Пользователь
 
Регистрация: 01.12.2015
Сообщений: 82
По умолчанию

ну вообще да...
но конкретно в данном случае из-за чего проблема?
Тут виснит не от нехватки озу, 60% заполненность...
niockasd вне форума Ответить с цитированием
Старый 04.05.2019, 22:26   #5
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Какая Delphi у вас?

А так если хотите работать уже с имеющимися занимаемыми объемами памяти то

Если XE то
Зайдите в Tools->Options->Editor Options->Undo Limit = поставьте более высокое значение это лимит по использованию памяти Delphi

В D7 то
Зайдите в Tools->Editor Propertioes->вкладка General->Undo Limit

Последний раз редактировалось Aliens_wolfs; 04.05.2019 в 22:34.
Aliens_wolfs вне форума Ответить с цитированием
Старый 04.05.2019, 22:35   #6
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Цитата:
Сообщение от niockasd Посмотреть сообщение
ну вообще да...
но конкретно в данном случае из-за чего проблема?
Тут виснит не от нехватки озу, 60% заполненность...
Так это не важно. Системная библиотека ищет непрерывный участок из 701 мб. Ей не важно что 1400 мб свободно. Она смотрит и видит что это 2 по 700 мб. Поэтому она и не может выделить память. И как следствие выдаёт ошибку.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 04.05.2019, 22:52   #7
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Сразу на диск записывайте в посте #2 я из вашего примера пример накидал и будет вам счастье, а то вы в память все принимаете конечно она растет.
Я через BlockWrite более 2гб принимал а память была неизменной

Последний раз редактировалось Aliens_wolfs; 04.05.2019 в 23:00.
Aliens_wolfs вне форума Ответить с цитированием
Старый 04.05.2019, 22:59   #8
niockasd
Пользователь
 
Регистрация: 01.12.2015
Сообщений: 82
По умолчанию

Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Какая Delphi у вас?

А так если хотите работать уже с имеющимися занимаемыми объемами памяти то

Если XE то
Зайдите в Tools->Options->Editor Options->Undo Limit = поставьте более высокое значение это лимит по использованию памяти Delphi

В D7 то
Зайдите в Tools->Editor Propertioes->вкладка General->Undo Limit
Embarcadero® Delphi 10.3 Version 26.0.32429.4364
niockasd вне форума Ответить с цитированием
Старый 04.05.2019, 23:02   #9
niockasd
Пользователь
 
Регистрация: 01.12.2015
Сообщений: 82
По умолчанию

Цитата:
Сообщение от Aliens_wolfs Посмотреть сообщение
Чтобы клиент не распухал нужно сразу на диск писать файл лучше использовать File через BlockWrite
Либо кусками через TMemoryStream принял почистил перешел в конец файла и так далее но это геморойно

примерно так на вашем примере
Код:
   
FFile: File;
............

function OpenFile(out AFile: File; const AFileName : UTF8String): longbool;
Var
 i: integer;
begin
Result:= false;
if (AFileName <> '') then
begin
 		{$I-}
		AssignFile ( AFile, AFileName );
		FileMode := fmOpenReadWrite;
		Reset ( AFile, 1 );
		{$I+}
		i := IOResult;
		If i = 5 Then
		Begin
			{$I-}
			AssignFile ( AFile, AFileName );
			FileMode := fmOpenWrite;
			rewrite ( AFile, 1 );
		 	{$I+}
			i := IOResult;
	 	End;
if i <> 0 then
 CloseFile(AFile)
 else
 Result:= true;
 end;
 end;

procedure TService.FileTransferSocketCleintRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  Rtext: ansistring; // Принятый текст
begin
  Rtext := Socket.ReceiveText;
  if FileTrasfer_Receive then // Если клиент в режиме приёма файла, то...
    Writing(RText) // Записываем данные в буфер
  else // Если клиент не в режиме приёма файла, то...
  begin

    if Copy(Rtext, 0, Pos('#', Rtext) -1) = 'file' then // Если это файл, то...
    begin
      FileTrasfer_MS := TMemoryStream.Create; // Создаём буфер для файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем имя файла

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

      Delete(Rtext, 1, Pos('#', Rtext)); // Определяем размер файла
      FileTrasfer_Size := StrToInt(Copy(Rtext, 0, Pos('#', Rtext) -1)); // Определяем размер файла
      Delete(Rtext, 1, Pos('#', Rtext)); // Удаляем последний разделитель

      FileTrasfer_Receive := OpenFile(FFile, FileTrasfer_Name); // Переводим сервер в режим приёма файла
      Writing(RText); // Записываем данные в буфер
    end;
  end;
end;

procedure Writing(Text: AnsiString);
begin
  if FileTrasfer_MS.Size < FileTrasfer_Size then // Если принято байт меньше размера файла, то...
     BlockWrite(FFile, Pointer(Text)^, Length(Text)); // Записываем буфер в файл

  if FileTrasfer_MS.Size = FileTrasfer_Size then  // Если файл принят, то...
  begin
    FileTrasfer_Receive := false; // Переводим клиента в нормальный режим

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

  //что то пошло не так принял больше чем ждал
  if FileTrasfer_MS.Size >= FileTrasfer_Size then // Если файл принят, то...
  begin
    Service.FileTransferSocketCleint.Socket.SendText('end'); // Посылаем команду "end", то есть файл принят
    CloseFile(FFile); // закрываем файл
  end;
end;
Большое спасибо за пример.
Пытаюсь понять как его прикрутить, судя по всему проблема где то в
процедуре Writing.

У меня же там проверка идет (FileTrasfer_MS.Size = FileTrasfer_Size ) а в данном случае я так понял эта переменная не заполняется и следовательно условие не исполняется..
Как теперь эту процедуру обновить? )
niockasd вне форума Ответить с цитированием
Старый 04.05.2019, 23:02   #10
Aliens_wolfs
Форумчанин
 
Регистрация: 16.12.2009
Сообщений: 902
По умолчанию

Найдите у себя в Delphi в настройках Undo Limit.

Цитата:
У меня же там проверка идет (FileTrasfer_MS.Size = FileTrasfer_Size ) а в данном случае я так понял эта переменная не заполняется и следовательно условие не исполняется..
извиняюсь поторопился я там не заметил

Проверку сделать можно так

либо до 1-гига так
fileSize(FFile) = FileTrasfer_Size

либо для всех объемов вот эта функция

Код:
function SizeFilesEx(const FileName : String): Int64;
var
  Handle   : THandle;
  FindData : TWin32FindData;
begin
FillChar(FindData, SizeOf(TWin32FindData), 0);
  Handle := FindFirstFile(PChar(FileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
  begin
    Windows.FindClose(Handle);
    if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
    begin
      Int64Rec(Result).Lo := FindData.nFileSizeLow;
      Int64Rec(Result).Hi := FindData.nFileSizeHigh;
      Exit;
    end;
  end;
  Result := -1;
end;
проверку делаете так
SizeFilesEx(FFile) = FileTrasfer_Size

Последний раз редактировалось Aliens_wolfs; 04.05.2019 в 23:39.
Aliens_wolfs вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
C# передача файлов в сети TaTT DoGG Помощь студентам 20 02.04.2017 15:53
Передача файлов по сети Shouldercannon Работа с сетью в Delphi 0 19.01.2016 15:30
передача файлов по сети SPD Общие вопросы Delphi 7 10.06.2011 00:24
Передача больших файлов чрез сокет D_E_N Работа с сетью в Delphi 0 18.01.2010 19:17
Передача файлов по сети SL1CK Работа с сетью в Delphi 3 05.12.2009 00:08