1

このコードを使用して、ファイルをクライアントに送信します。

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 はすぐに実行スレッドを終了するため、ファイルハンドルはまだ開いています! クライアントが切断された後にファイルハンドルを閉じる方法はありますか?

ご協力ありがとうございました。

4

1 に答える 1

6

コードには3つの問題があります。

1)TMemoに直接アクセスすることはスレッドセーフではないため、デッドロックやクラッシュを引き起こす可能性があります。UIアクセスは、メインスレッドのコンテキストでのみ実行する必要があります。IndyTIdSyncまたはTIdNotifyクラスを使用して、サーバーイベントから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; 
于 2012-08-11T01:07:01.483 に答える