求助,delphi大文件传输最后总会内存溢出

风木菌 2017-11-03 10:32:58
求助大神帮忙,遇到了一个问题,用indy10写的文件传输函数,没有用线程,估计也不需要。传输小文件的时候可以,可是超过一定大小就会i不定时发生内存溢出的现象,主要是在服务器端发生的。小弟真的很无奈了,求助。

客户端代码

unit client_new;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,IdGlobal,
IdComponent, IdTCPConnection, IdTCPClient, Vcl.ComCtrls, IdAntiFreezeBase,
IdAntiFreeze;


type
TMyRecord = record
Details:string[255];
FileName:string[255];
FileSize:integer;
FileDate:TDateTime;
RecordSize:Integer;
end;


type
TForm1 = class(TForm)
chkConnect: TCheckBox;
edHost: TEdit;
IdTCPClient1: TIdTCPClient;
Memo1: TMemo;
edSendFile: TEdit;
btnGetFile: TButton;
btnSendFile: TButton;
OpenDialog1: TOpenDialog;
ProgressBar1: TProgressBar;
IdAntiFreeze1: TIdAntiFreeze;
procedure chkConnectClick(Sender: TObject);
procedure btnGetFileClick(Sender: TObject);
procedure btnSendFileClick(Sender: TObject);
procedure IdTCPClient1Connected(Sender: TObject);
procedure IdTCPClient1Disconnected(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure SetClientState(sState:Boolean);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnGetFileClick(Sender: TObject);
begin
OpenDialog1.Execute();
edSendFile.Text := OpenDialog1.FileName ;
end;

procedure TForm1.btnSendFileClick(Sender: TObject);
var
AFileHandler : integer;
AMyRecord : TMyRecord;
Abuf:TidBytes;
AStream:TFileStream;
ASize:int64;
cmt:int64;
Bbuf:array [0..4096] of byte;
begin
//先验证文件是否存在
if FileExists(edSendFile.Text) = false then
begin
showmessage('No File');
exit;
end;

//获得文件信息
AFileHandler:=FileOpen(edSendFile.Text,fmOpenRead);
//存储信息在record中
AMyRecord.FileName := extractfilename(edSendFile.Text);
AMyRecord.FileDate := now;
AMyRecord.FileSize := FileSeek(AFileHandler,0,2);
AMyRecord.Details := 'file test';
AMyRecord.RecordSize := sizeof(AMyRecord);
FileSeek(AFileHandler,0,0);
// FileClose(AFileHandler);

//传输record,需要转换为buf发送
Abuf := RawToBytes(AMyRecord,sizeof(AMyRecord));
IdTCPClient1.IOHandler.Write(int64(length(Abuf)));
IdTCPClient1.IOHandler.WriteBufferOpen ;
IdTCPClient1.IOHandler.Write(Abuf,length(Abuf));
// IdTCPClient1.IOHandler.WriteBufferFlush;
// IdTCPCLient1.IOHandler.WriteBufferClose;

//获得反馈信息,默认服务器直接接收文件,先获得流,再做事儿
// AStream:=TFileStream.Create;
//get stream from file
// AStream.LoadFromFile(edSendFile.Text); //这是一个败笔,不应该这样做,更何况也无法一次性读取文件
//正确的办法是按照一定大小读文件到流,然后传输流,问题成为如何分段读入文件到流。

// AStream.Position := 0;

//获得流大小,并将其拆分为4096小块发送
// ASize := AStream.Size ;
ASize := AMyRecord.FileSize;
IdTCPClient1.IOHandler.Write(ASize);

// IdTCPClient1.IOHandler.Write(AStream,ASize,false ); //good job

ProgressBar1.Max := ASize;
ProgressBar1.Position := 0;
Memo1.Lines.Add('File Sent@'+TimeToStr(now));
Application.ProcessMessages;//啥作用呢?
// IdTCPClient1.IOHandler.WriteBufferOpen;
//需弄清楚此处的限制条件是针对什么,应该针对传出的内容而定。
while true do
begin
cmt := FileRead(AFileHandler,Bbuf,4096);//read 4096 byte from file;
IdTCPClient1.IOHandler.Write(cmt);
Abuf := RawToBytes(Bbuf,cmt);
IdTCPClient1.IOHandler.Write(Abuf,length(Abuf));//there is no difference between Abuf and Bbuf except type
//这样仍然解决不了大文件传输的问题,报错原因是内存过大。

//将流写入buf
// cmt := AStream.Read(Bbuf,4096);//读入流中,然后传递
// IdTCPClient1.IOHandler.Write(cmt);
// Abuf:=rawToBytes(Bbuf,cmt);
// IdTCPClient1.IOHandler.Write(Abuf,length(Abuf));
ProgressBar1.Position := ProgressBar1.Position + cmt;
if cmt<4096 then
break;

end;
// AStream.Write(Bbuf,ASize);
// cmt:= sizeof(Bbuf);
// IdTCPClient1.IOHandler.Write(cmt);
// Abuf:=rawToBytes(Bbuf,cmt);
// IdTCPClient1.IOHandler.Write(Abuf,length(Abuf));
// ProgressBar1.Position := ProgressBar1.Position + ASize;
IdTCPClient1.IOHandler.WriteBufferFlush;
IdTCPClient1.IOHandler.WriteBufferClose;
// AStream.Free;
FileClose(AFileHandler);

Memo1.Lines.Add('Server receive file @'+IdTCPClient1.IOHandler.ReadLn());
ProgressBar1.Position :=0;
end;

procedure TForm1.chkConnectClick(Sender: TObject);
begin
if chkConnect.Checked = true then
SetClientState(true)
else
SetClientState(false);

end;

procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
Memo1.Lines.Add('Client Connect');
end;

procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
Memo1.Lines.Add('Client Disconnect');
end;

procedure TForm1.SetClientState(sState: Boolean);
begin
if sState = true then
begin
IdTCPClient1.Host := edHost.Text;
IdTCPClient1.Port := 8800;
IdTCPClient1.Connect;
end
else
IdTCPClient1.Disconnect ;


chkConnect.Checked := sState;
end;

end.


服务器代码

unit server_new;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, IdBaseComponent, IdComponent,IdGlobal,
IdCustomTCPServer, IdTCPServer, Vcl.StdCtrls, Vcl.ComCtrls;

type
TMyRecord = record
Details:string[255];
FileName:string[255];
FileSize:integer;
FileDate:TDateTime;
RecordSize:Integer;
end;

type
TForm1 = class(TForm)
chkStart: TCheckBox;
Memo1: TMemo;
IdTCPServer1: TIdTCPServer;
ProgressBar1: TProgressBar;
procedure IdTCPServer1Connect(AContext: TIdContext);
procedure chkStartClick(Sender: TObject);
procedure IdTCPServer1Disconnect(AContext: TIdContext);
procedure IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
procedure IdTCPServer1Execute(AContext: TIdContext);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.chkStartClick(Sender: TObject);
begin
IdTCPServer1.DefaultPort := 8800;
idTCPServer1.Active := chkStart.Checked;
end;

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
Memo1.Lines.Add('Client connect');
end;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
Memo1.Lines.Add('Client Disconnect');
end;

procedure TForm1.IdTCPServer1Exception(AContext: TIdContext;
AException: Exception);
begin
Memo1.Lines.Add('Exception '+ AException.Message );
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
AMyRecord:TMyRecord;
ASize:int64;
cmt:int64;
Abuf:TIdBytes;
Bbuf:array[0..4096] of byte;
AStream:TFileStream;
begin
//获得信息,并进行转换
// ASize := AContext.Connection.IOHandler.ReadInt64();


with AContext.Connection.IOHandler do
begin
ASize := ReadInt64();
ReadBytes(Abuf,ASize,false);
BytesToRaw(Abuf,AMyRecord,sizeof(AMyRecord));
//将收到的信息写出来
Memo1.Lines.Add('Receive record:');
Memo1.Lines.Add('FileName = ' + AMyRecord.FileName);
Memo1.Lines.Add('FileSize = ' + IntToStr(AMyRecord.FileSize));
Memo1.Lines.Add('Date = ' + DateToStr(AMyRecord.FileDate));
Memo1.Lines.Add('Time = ' + TimeToStr(AMyRecord.FileDate));
Memo1.Lines.Add('RecordSize = ' + IntToStr(AMyRecord.Recordsize));
//继续传输文件到流中

if AMyRecord.Details <> 'file test' then
begin
Memo1.Lines.Add('Record Receive successfully');
exit;
end;
AStream:=TFileStream.Create(AMyRecord.FileName,fmCreate); //此处没有该文件,能否这样作呢
ASize := ReadInt64();

// ReadStream(AStream,ASize,false);
ProgressBar1.Position := 0;
ProgressBar1.Max := ASize;

while ASize>4096 do //the biggest problem is the stream cannot be too big
begin

cmt := ReadInt64();
ReadBytes(Abuf,cmt);
BytesToraw(Abuf,Bbuf,sizeof(Bbuf));
// AStream.Write(Bbuf,4096);
AStream.Write(Bbuf,4096); //写入内存,实际上还是应该直接写入文件才行
ProgressBar1.Position := ProgressBar1.Position + cmt;
inc(ASize,-4096);
end;
cmt := ReadInt64();
ReadBytes(Abuf,cmt);
BytesToraw(Abuf,Bbuf,sizeof(Bbuf));
AStream.Write(Bbuf,cmt);
ProgressBar1.Position := ProgressBar1.Position + cmt;
// AStream.SaveToFile(AMyRecord.FileName);

AStream.Free;
Memo1.Lines.Add('File saved @'+TimeToStr(now));
writeln(TimeToStr(now));
showmessage('file receiving over');
ProgressBar1.Position:=0;
end;

end;

end.
...全文
704 6 打赏 收藏 转发到动态 举报
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
leeky 2017-11-07
  • 打赏
  • 举报
回复
引用 5 楼 TreeOutWind 的回复:
[quote=引用 2 楼 qq_38204686 的回复:] 1.buf大一点 2.分段传输
谢谢,不过,对于分段传输的策略不是很清楚,能进一步指点下吗?[/quote] 我说的不一定是最好的方法,但是可行的。 1、文件操作就用TFileStream,使用它的方法create、read(发送端)、write(接收端)、seek(双方都要调用), 网络数据的传输可以用byte数组等呀等。 2、注意文件长度为0的,注意最末一个包长度不是指定长度的情形,注意发送方是否重复发送的情形、注意数据校验(做第一个版本可以不做此功能,但我们的经验是传输总会有出错的时候) 总之自定义传输细节方面很多需要注意。
风木菌 2017-11-06
  • 打赏
  • 举报
回复
引用 2 楼 qq_38204686 的回复:
1.buf大一点 2.分段传输
谢谢,不过,对于分段传输的策略不是很清楚,能进一步指点下吗?
风木菌 2017-11-06
  • 打赏
  • 举报
回复
引用 1 楼 leeky 的回复:
传文件,不要偷懒,可以一段段地传,应答式传输,一次传几K。 自定义协议
谢谢方案,我想知道,分段传输的一般策略是什么?先将文件变成文件流,然后分段传输?还是直接将文件分段传输?这两种策略有何差异吗?
lyhoo163 2017-11-05
  • 打赏
  • 举报
回复
你可以建立FTP传输。效果好些。
大米粥哥哥 2017-11-03
  • 打赏
  • 举报
回复
1.buf大一点 2.分段传输
leeky 2017-11-03
  • 打赏
  • 举报
回复
传文件,不要偷懒,可以一段段地传,应答式传输,一次传几K。 自定义协议

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧