Delphi Indy TCPClient将文件发送到TCPServer,并使用空格和@字符
我刚开始和印第一起“玩”。使用此帖子并对其进行了修改,使我的客户机成为向服务器发送数据的人 客户端具有以下代码来发送数据:Delphi Indy TCPClient将文件发送到TCPServer,并使用空格和@字符,delphi,indy,Delphi,Indy,我刚开始和印第一起“玩”。使用此帖子并对其进行了修改,使我的客户机成为向服务器发送数据的人 客户端具有以下代码来发送数据: procedure TForm1.btnConnectClick(Sender: TObject); begin TCPClient.Host:='192.168.88.117'; TCPClient.Port:=32832; TCPClient.Connect; end; procedure TForm1.btnSendClick(Sender: TOb
procedure TForm1.btnConnectClick(Sender: TObject);
begin
TCPClient.Host:='192.168.88.117';
TCPClient.Port:=32832;
TCPClient.Connect;
end;
procedure TForm1.btnSendClick(Sender: TObject);
var
AStream : TFileStream;
begin
if not TCPClient.Connected then Exit;
TCPClient.IOHandler.WriteLn('SEND_FILE '+GetComputerName+FormatDateTime('yyyy-mm-dd_hhnnsszzz',now)+'.txt');
try
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\1.txt', fmOpenRead or fmShareDenyWrite);
TCPClient.IOHandler.LargeStream := true;
TCPClient.IOHandler.Write(AStream, 0, True);
finally
AStream.Free;
end;
TCPClient.Disconnect; // otherwise the file is locked on the server side
end;
服务器具有以下代码来接收数据:
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename : string;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFileName(params);
TThread.Queue(nil,
procedure
begin
Memo1.Lines.Add('Command '+cmd+' File Name '+filename);
end
);
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+filename, fmCreate);
try
AContext.Connection.IOHandler.LargeStream:=true;
AContext.Connection.IOHandler.ReadStream(AStream, -1, false);
finally
AStream.Free;
end;
end;
end;
它似乎运行正常,我为我的测试创建了两个客户机,并从不同的计算机上运行它们,服务器从这两个客户机接收数据并正确创建数据
我只有两个问题:
1。由于某些原因,服务器上接收到的文件始终具有相同数量的前导空格,后跟@符号
原始文件如下所示
HERE IS SOME STUFF
服务器上收到的文件如下所示:
@HERE IS SOME STUFF
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename : string;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFileName(params);
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+filename, fmCreate);
try
AContext.Connection.IOHandler.WriteLn('OK');
AContext.Connection.IOHandler.LargeStream:=true;
AContext.Connection.IOHandler.ReadStream(AStream, -1, false);
finally
AStream.Free;
end;
end;
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SENT' then
RenameFile(filename,ChangeFileExt(filename,'.dat'));
end;
procedure TForm1.BackgroundTransfer;
var
TCPClient : TidTCPClient;
AStream : TFileStream;
Files : TStringDynArray;
i : integer;
isDone : boolean;
begin
Files := TDirectory.GetFiles(ExtractFilePath(Application.ExeName)+'\1\','*.out');
TThread.Queue(TThread.CurrentThread,
procedure
begin
Memo1.Lines.Add(IntToStr(Length(Files)));
end
);
if Length(Files) = 0 then Exit;
TCPClient := TidTCPClient.Create(nil);
try
TransferActive:=true;
TCPClient.Disconnect;
TCPClient.Host:='192.168.88.117';
TCPClient.Port:=32832;
TCPClient.Connect;
for i := Low(Files) to High(Files) do
begin
isDone:=false;
if FileExists(Files[i]) = true then
begin
TCPClient.IOHandler.WriteLn('SEND_FILE '+Files[i]);
try
// wait for server
repeat
sleep(100);
if TCPClient.IOHandler.ReadLn() = 'OK' then break;
until ( true );
AStream := TFileStream.Create(Files[i], fmOpenRead or fmShareDenyWrite);
TCPClient.IOHandler.LargeStream := true;
TCPClient.IOHandler.Write(AStream, 0, True);
isDone:=true;
TCPClient.IOHandler.WriteLn('SENT '+Files[i]);
finally
AStream.Free;
end;
if isDone = true then
System.SysUtils.DeleteFile(Files[i]);
end;
end;
finally
TCPClient.Free;
TransferActive:=false;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
if TransferActive = false then
begin
inc(ThreadTimer);
Panel1.Caption:=ThreadTimer.ToString;
Panel1.Color:=clRed;
end
else
begin
Panel1.Color:=clGreen;
end;
if ThreadTimer >= 10 then
begin
ThreadTimer:=0;
TransferTask := TTask.Create(BackGroundTransfer);
TransferTask.Start;
end;
end;
2.每次发送文件后,我都需要断开连接,否则Indy TCPServer会将文件锁定,这是预期的行为吗?我如何判断文件是否已完成?我需要在另一个线程中逐个处理接收到的文件
多谢各位
更新1
根据Remy的建议,我对服务器进行了如下更改:
@HERE IS SOME STUFF
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename : string;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFileName(params);
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+filename, fmCreate);
try
AContext.Connection.IOHandler.WriteLn('OK');
AContext.Connection.IOHandler.LargeStream:=true;
AContext.Connection.IOHandler.ReadStream(AStream, -1, false);
finally
AStream.Free;
end;
end;
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SENT' then
RenameFile(filename,ChangeFileExt(filename,'.dat'));
end;
procedure TForm1.BackgroundTransfer;
var
TCPClient : TidTCPClient;
AStream : TFileStream;
Files : TStringDynArray;
i : integer;
isDone : boolean;
begin
Files := TDirectory.GetFiles(ExtractFilePath(Application.ExeName)+'\1\','*.out');
TThread.Queue(TThread.CurrentThread,
procedure
begin
Memo1.Lines.Add(IntToStr(Length(Files)));
end
);
if Length(Files) = 0 then Exit;
TCPClient := TidTCPClient.Create(nil);
try
TransferActive:=true;
TCPClient.Disconnect;
TCPClient.Host:='192.168.88.117';
TCPClient.Port:=32832;
TCPClient.Connect;
for i := Low(Files) to High(Files) do
begin
isDone:=false;
if FileExists(Files[i]) = true then
begin
TCPClient.IOHandler.WriteLn('SEND_FILE '+Files[i]);
try
// wait for server
repeat
sleep(100);
if TCPClient.IOHandler.ReadLn() = 'OK' then break;
until ( true );
AStream := TFileStream.Create(Files[i], fmOpenRead or fmShareDenyWrite);
TCPClient.IOHandler.LargeStream := true;
TCPClient.IOHandler.Write(AStream, 0, True);
isDone:=true;
TCPClient.IOHandler.WriteLn('SENT '+Files[i]);
finally
AStream.Free;
end;
if isDone = true then
System.SysUtils.DeleteFile(Files[i]);
end;
end;
finally
TCPClient.Free;
TransferActive:=false;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
if TransferActive = false then
begin
inc(ThreadTimer);
Panel1.Caption:=ThreadTimer.ToString;
Panel1.Color:=clRed;
end
else
begin
Panel1.Color:=clGreen;
end;
if ThreadTimer >= 10 then
begin
ThreadTimer:=0;
TransferTask := TTask.Create(BackGroundTransfer);
TransferTask.Start;
end;
end;
在客户端上,我使用ITask将整个传输移动到一个线程中,如下所示:
@HERE IS SOME STUFF
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename : string;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFileName(params);
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+filename, fmCreate);
try
AContext.Connection.IOHandler.WriteLn('OK');
AContext.Connection.IOHandler.LargeStream:=true;
AContext.Connection.IOHandler.ReadStream(AStream, -1, false);
finally
AStream.Free;
end;
end;
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SENT' then
RenameFile(filename,ChangeFileExt(filename,'.dat'));
end;
procedure TForm1.BackgroundTransfer;
var
TCPClient : TidTCPClient;
AStream : TFileStream;
Files : TStringDynArray;
i : integer;
isDone : boolean;
begin
Files := TDirectory.GetFiles(ExtractFilePath(Application.ExeName)+'\1\','*.out');
TThread.Queue(TThread.CurrentThread,
procedure
begin
Memo1.Lines.Add(IntToStr(Length(Files)));
end
);
if Length(Files) = 0 then Exit;
TCPClient := TidTCPClient.Create(nil);
try
TransferActive:=true;
TCPClient.Disconnect;
TCPClient.Host:='192.168.88.117';
TCPClient.Port:=32832;
TCPClient.Connect;
for i := Low(Files) to High(Files) do
begin
isDone:=false;
if FileExists(Files[i]) = true then
begin
TCPClient.IOHandler.WriteLn('SEND_FILE '+Files[i]);
try
// wait for server
repeat
sleep(100);
if TCPClient.IOHandler.ReadLn() = 'OK' then break;
until ( true );
AStream := TFileStream.Create(Files[i], fmOpenRead or fmShareDenyWrite);
TCPClient.IOHandler.LargeStream := true;
TCPClient.IOHandler.Write(AStream, 0, True);
isDone:=true;
TCPClient.IOHandler.WriteLn('SENT '+Files[i]);
finally
AStream.Free;
end;
if isDone = true then
System.SysUtils.DeleteFile(Files[i]);
end;
end;
finally
TCPClient.Free;
TransferActive:=false;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
if TransferActive = false then
begin
inc(ThreadTimer);
Panel1.Caption:=ThreadTimer.ToString;
Panel1.Color:=clRed;
end
else
begin
Panel1.Color:=clGreen;
end;
if ThreadTimer >= 10 then
begin
ThreadTimer:=0;
TransferTask := TTask.Create(BackGroundTransfer);
TransferTask.Start;
end;
end;
我每10秒用计时器检查一次任务是否正在运行,如果没有,我会创建一个新任务,如下所示:
@HERE IS SOME STUFF
procedure TForm1.TCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename : string;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFileName(params);
AStream := TFileStream.Create(ExtractFilePath(Application.ExeName)+'\'+filename, fmCreate);
try
AContext.Connection.IOHandler.WriteLn('OK');
AContext.Connection.IOHandler.LargeStream:=true;
AContext.Connection.IOHandler.ReadStream(AStream, -1, false);
finally
AStream.Free;
end;
end;
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SENT' then
RenameFile(filename,ChangeFileExt(filename,'.dat'));
end;
procedure TForm1.BackgroundTransfer;
var
TCPClient : TidTCPClient;
AStream : TFileStream;
Files : TStringDynArray;
i : integer;
isDone : boolean;
begin
Files := TDirectory.GetFiles(ExtractFilePath(Application.ExeName)+'\1\','*.out');
TThread.Queue(TThread.CurrentThread,
procedure
begin
Memo1.Lines.Add(IntToStr(Length(Files)));
end
);
if Length(Files) = 0 then Exit;
TCPClient := TidTCPClient.Create(nil);
try
TransferActive:=true;
TCPClient.Disconnect;
TCPClient.Host:='192.168.88.117';
TCPClient.Port:=32832;
TCPClient.Connect;
for i := Low(Files) to High(Files) do
begin
isDone:=false;
if FileExists(Files[i]) = true then
begin
TCPClient.IOHandler.WriteLn('SEND_FILE '+Files[i]);
try
// wait for server
repeat
sleep(100);
if TCPClient.IOHandler.ReadLn() = 'OK' then break;
until ( true );
AStream := TFileStream.Create(Files[i], fmOpenRead or fmShareDenyWrite);
TCPClient.IOHandler.LargeStream := true;
TCPClient.IOHandler.Write(AStream, 0, True);
isDone:=true;
TCPClient.IOHandler.WriteLn('SENT '+Files[i]);
finally
AStream.Free;
end;
if isDone = true then
System.SysUtils.DeleteFile(Files[i]);
end;
end;
finally
TCPClient.Free;
TransferActive:=false;
end;
end;
procedure TForm1.Timer2Timer(Sender: TObject);
if TransferActive = false then
begin
inc(ThreadTimer);
Panel1.Caption:=ThreadTimer.ToString;
Panel1.Color:=clRed;
end
else
begin
Panel1.Color:=clGreen;
end;
if ThreadTimer >= 10 then
begin
ThreadTimer:=0;
TransferTask := TTask.Create(BackGroundTransfer);
TransferTask.Start;
end;
end;
如果我理解正确的话,Indy会跟踪背景中的连接。连接A-A、B-B、C-C,它们不能混淆。我这样问是因为从服务器和客户端我只发送OK或send,它就可以工作了。(希望不仅仅是运气好)
这很重要,因为一旦它完全工作,我将有多个客户端(android设备)将数据发送到此服务器。而且有时会有一个以上的客户端上传数据的可能性很高
我还测试了这样的情况:如果我开始将文件复制到客户机输入目录中,但复制没有完成,而是任务开始了。它工作正常,第一次运行时检测到350个文件,下一次运行时检测到500个文件
还测试了如果我简单地停止服务器,会发生什么也可以工作。如果我使用TCPServer.Active:=false
在客户端,如果连接丢失,WriteLn和ReadLn会导致异常(我猜是服务器超时)
在服务器端,一旦完成,我只需将接收到的文件从OUT重命名为DAT。我不能100%确定这是否保证文件确实100%正确传输。然而,我无法在测试期间生成损坏的文件
总之,整个想法是:
TCPClient运行在Android手机上,用户可以扫描条形码,我从中创建一个控制文件,然后每隔10秒上传到服务器。然后由服务器处理并发送到另一台服务器
问候
更新2
已从服务器中删除此行:
DelFilesFromDir(ExtractFilePath(Application.ExeName), '*.out', FALSE);
在Execute方法中加入是个坏主意
我需要找到另一种方法来删除可能的垃圾文件
TransferActive也是一个全局变量,正在被Transfer后台线程修改。但由于一次只运行一个线程,我认为这应该是安全的。1)此代码中没有任何内容会导致收到的文件中出现额外的字节。如果源文件中不存在这些字节,则Indy之外的其他内容必须干扰数据。2) 不,您不需要断开连接即可完成传输。您告诉
Write()
在文件数据之前发送文件大小,并告诉ReadStream()
在数据之前读取该大小。这是最好的,并确保TCP连接在传输完成后仍然可以使用。当t文件流
被销毁时,接收到的文件被解锁。确保这一点。根据所示代码,我看不出你所描述的是如何发生的,无论是对于#1还是#2,除非有外部干扰。代码本身很好。尽管如此,我还是建议让客户端在发送SEND_file
命令之前打开源文件,并让服务器在创建目标文件之后向客户端发送成功/失败回复,然后再让客户端发送文件数据。另一方面,ExtractFilePath()
为您提供了一个尾随反斜杠,你真的不应该用你的应用程序文件夹来写文件。使用SHGetFolderPath()
或SHGetKnownFolderPath()
请求Windows在您的用户配置文件中提供一个文件夹,保证您具有写入权限。Remy感谢您的帮助,问题1和问题2似乎不再存在。我不知道我做了什么,但在编写了第一个Android客户端,然后回到旧的windows项目之后,问题就消失了。可能是因为我在测试过程中做错了什么。无论如何,现在我已经更新了代码,它似乎工作正常。但是如果您可以对其进行注释,那就太好了:)ExtractFilePath(Application.ExeName)仅用于开发版本。在Android上,我使用的是“Home”目录。