【问题标题】:Indy sending file and close open handle(s)Indy 发送文件并关闭打开的句柄
【发布时间】:2012-08-10 23:33:03
【问题描述】:

我使用此代码向客户端发送文件。

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
 hFile    : THandle;
 FileBuff : array [0..1023] of byte;
 dwRead   : DWORD;
 Recieved : String;
begin
 Recieved := Athread.Connection.ReadLn;
 if Recieved = 'FILE' then begin
   Memo1.Lines.Add('Sending File...');
   hFile := CreateFileW('FILE.bin',
   GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
   if hFile = INVALID_HANDLE_VALUE then exit;
   SetFilePointer(hFile, 0, nil, FILE_BEGIN);
   while true do begin
     ReadFile(hFile, FileBuff[0], 1024, dwRead, nil);
     if dwRead <= 0 then break;
     Athread.Connection.WriteBuffer(FileBuff[0], dwRead);
   end;
   CloseHandle (hFile);
   Athread.Connection.Disconnect;
 end;
end;

这就像一个魅力,但如果客户端在文件发送时断开连接,Indy 会立即终止执行线程,因此文件句柄仍然打开!有没有办法在客户端断开连接后关闭文件句柄?

感谢您的帮助。

【问题讨论】:

  • 您是否尝试过使用try..finally..end

标签: delphi-7 indy delphi indy-9


【解决方案1】:

您的代码存在三个问题:

1) 直接访问 TMemo 不是线程安全的,因此可能会导致死锁和/或崩溃。 UI 访问必须仅在主线程的上下文中完成。您可以使用 Indy 的 TIdSyncTIdNotify 类从服务器事件中安全地访问 UI。

2) 就像提到的 RRUZ 一样,您没有保护文件句柄免受异常影响。如果引发异常,例如当客户端断开连接时,您并没有关闭文件句柄。

3) 您正在使用相对路径打开文件。始终使用绝对路径以确保使用正确的文件。

试试这个:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); 
var 
 hFile    : THandle; 
 FileBuff : array [0..1023] of byte; 
 dwRead   : DWORD; 
 Recieved : String; 
begin 
  Recieved := Athread.Connection.ReadLn; 
  if Recieved = 'FILE' then begin 
    // I'll leave this as an exercise for you to figure out...
    //Memo1.Lines.Add('Sending File...'); 

    hFile := CreateFile('C:\Path\FILE.bin', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); 
    if hFile = INVALID_HANDLE_VALUE then RaiseLastOSError;
    try
      repeat 
        if not ReadFile(hFile, FileBuff[0], SizeOf(FileBuff), dwRead, nil) then RaiseLastOSError;
        if dwRead = 0 then Break; 
        AThread.Connection.WriteBuffer(FileBuff[0], dwRead);
      until False;
    finally
      CloseHandle(hFile); 
    end;
    AThread.Connection.Disconnect; 
  end; 
end; 

或者,您可以将文件名传递给 Indy 并让它为您管理文件:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); 
var 
 Recieved : String; 
begin 
  Recieved := Athread.Connection.ReadLn; 
  if Recieved = 'FILE' then begin 
    // I'll leave this as an exercise for you to figure out...
    //Memo1.Lines.Add('Sending File...'); 

    AThread.Connection.WriteFile('C:\Path\FILE.bin', True); 
    AThread.Connection.Disconnect; 
  end; 
end; 

【讨论】:

  • 看起来不错!非常感谢你。我只是不知道 try except 在那种情况下实际上会被调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多