Search code examples
delphitfilestream

File not received if I free the instance of filestream anywhere?


I am trying to send a file using TServerSocket/TClientSocket. The file is sent completely as long as I do not free the filestream anywhere and by anywhere I mean form.OnCreate event too. If I do free anywhere only 1 or 2 percent is sent.

I also have to put the TFileStream.Create line of code on the server side OnCreate event. If I create a stream in TForm2.ServerSocket1ClientRead then I get an EFcreateerror: 'the process cannot access file because it is being used by another process''.

procedure TForm2.FormCreate(Sender: TObject);
begin
    FStream := TFileStream.Create('c:\temp\log.txt', fmCreate or
    fmShareDenyWrite);
end;

procedure TForm2.ClientSocket1Connect(Sender: TObject;
    Socket: TCustomWinSocket);
var
  fs: TFileStream;

begin
    fs := TFileStream.Create('c:\log.txt', fmOpenRead);
    socket.SendStream(fs);
end;

procedure TForm2.ServerSocket1ClientRead(Sender: TObject;
    Socket: TCustomWinSocket);
var
    iLen: Integer;
    Bfr: Pointer;
begin
    iLen := Socket.ReceiveLength;
    GetMem(Bfr, iLen);
    Socket.ReceiveBuf(Bfr^, iLen);
    FStream.Write(Bfr^, iLen);
    FreeMem(bfr);
    //fstream.free
end;

Even if I put my code this way:

if fstream.Size = fstream.position then
    fstream.free

Even then it gives me problem.

What is this strange phenomena? Is it a bug in Delphi? If yes is there a work-around? If it matters: I am using Delphi 2010.

Update: sorry I meant if I put my code this way:

if fileSize = fstream.position then
    fstream.free

Sorry, not fstream.size but filesize. I have already initialised file size as 300000 (size of file to be received).

Solved: Solved by replacing

FStream := TFileStream.Create('c:\temp\log.txt',
                              fmCreate or fmShareDenyWrite);

with

if not FileExists('c:\temp\log.txt') then
    FStream := TFileStream.Create('c:\temp\log.txt',
                                  fmCreate or fmShareDenyWrite);

Solution

  • You are trying to free your FStream object as soon as you receive the first block of data. Do not do that. That block will usually be smaller than the full file, especially if you are sending a large file. Also, checking for Position = Size on the receiving end is useless as well, as it will always evaluate be true since the current Position will always be at the end of the stream. As I already told you in the other discussion, you are not using the SendStream() and ReceiveBuf() methods effectively, and the sender needs to send the file size before sending the file data (or alternatively disconnect at the end of the file) so the receiver knows exactly when to stop its reading.

    Edit: Try something like this:

    type
      TSocketBuffer = class
      public
        Stream: TStream;
        ExpectedSize: Int64;
        Data: array[0..1023] of Byte;
        DataOffset, DataSize: Integer;
        destructor Destroy; override;
      end;
    
      TServerSocketBuffer = class(TSocketBuffer)
      public
        FileName: String;
        destructor Destroy; override;
      end;
    
    destructor TSocketBuffer.Destroy;
    begin
      if Stream <> nil then Stream.Free;
      inherited;
    end;
    
    destructor TServerSocketBuffer.Destroy;
    begin
      if Stream <> nil then FreeAndNil(Stream);
      if FileName <> '' then DeleteFile(FileName);
      inherited;
    end;
    
    procedure TForm2.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); 
    var
      Buffer: TSocketBuffer;
    begin 
      Buffer := TSocketBuffer.Create;
      Socket.Data := Buffer;
    
      // open the file to send...
      Buffer.Stream := TFileStream.Create('c:\log.txt', fmOpenRead or fmShareDenyWrite); 
      Buffer.ExpectedSize := Buffer.Stream.Size;
    
      // buffer the stream size...
      Move(Buffer.Data[0], Buffer.ExpectedSize, Sizeof(Int64));
      Buffer.DataOffset := 0;
      Buffer.DataSize := SizeOf(Int64);
    
      // begin sending...
      ClientSocket1Write(Sender, Socket);
    end; 
    
    procedure TForm2.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket); 
    begin 
      TSocketBuffer(Socket.Data).Free;
    end; 
    
    procedure TForm2.ClientSocket1Write(Sender: TObject; Socket: TCustomWinSocket); 
    var
      Buffer: TSocketBuffer;
      NumBytes: Integer;
    begin 
      // in case OnWrite is fired before OnConnect...
      if Socket.Data = nil then Exit;
    
      Buffer := TSocketBuffer(Socket.Data);
      if Buffer.Stream = nil then Exit;
    
      // keep sending until EOF is reached, or until the socket blocks/errors...
      repeat
        // if there is pending data buffered, send it now...
        while Buffer.DataOffset < Buffer.DataSize do
        begin
          NumBytes := Socket.SendBuf(Buffer.Data[Buffer.DataOffset], Buffer.DataSize-Buffer.DataOffset);
          if NumBytes <= 0 then Exit; // wait for next event...
          Inc(Buffer.DataOffset, NumBytes);
        end;
    
        // has EOF been reached?
        if Buffer.ExpectedSize <= 0 then Break;
    
        // read the next block of data from the stream...
        Buffer.DataOffset := 0;
        Buffer.DataSize := 0;
        NumBytes := Buffer.Stream.Read(Buffer.Data[0], Min(Buffer.ExpectedSize, SizeOf(Buffer.Data)));
        if NumBytes <= 0 then Break; // stream error, stop sending...
        Buffer.DataSize := NumBytes;
        Dec(Buffer.ExpectedSize, NumBytes);
    
        // the next loop iteration will start sending it...
      until False;
    
      // all done...
      FreeAndNil(Buffer.Stream);
      Socket.Close;
    end; 
    
    procedure TForm2.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); 
    begin
      Socket.Data := TServerSocketBuffer.Create;
    end;
    
    procedure TForm2.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); 
    begin
      TServerSocketBuffer(Socket.Data).Free;
    end;
    
    procedure TForm2.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); 
    var 
      Buffer: TServerSocketBuffer;
      FileName: String;
      NumBytes: Integer; 
    begin 
      Buffer := TServerSocketBuffer(Socket.Data);
    
      if Buffer.Stream = nil then
      begin
        // keep reading until stream size has been received in full...
    
        while Buffer.DataSize < SizeOf(Int64) do
        begin
          NumBytes := Socket.ReceiveBuf(Buffer.Data[Buffer.DataOffset], SizeOf(Int64)-Buffer.DataOffset);
          if NumBytes <= 0 then Exit; // wait for next event...
          Inc(Buffer.DataSize, NumBytes);
          Inc(Buffer.DataOffset, NumBytes);
        end;
    
        Move(Buffer.ExpectedSize, Buffer.Data[0], SizeOf(Int64));
    
        // create the file to store in...
        FileName := 'c:\temp\log.txt';
        Buffer.Stream := TFileStream.Create(FileName, fmCreate);
        Buffer.FileName := FileName;
    
        // (optional) pre-size the file...
        Buffer.Stream.Size := Buffer.ExpectedSize;
      end;
    
      // keep reading until EOF is reached, or until the socket blocks/errors...
      while Buffer.ExpectedSize > 0 do
      begin
        // read the next block of data from the socket...
        Buffer.DataOffset := 0;
        Buffer.DataSize := 0;
    
        NumBytes := Socket.ReceiveBuf(Buffer.Data[0], Min(Buffer.ExpectedSize, SizeOf(Buffer.Data)));
        if NumBytes <= 0 then Exit; // wait for next event...
    
        Buffer.DataSize := NumBytes;
    
        // save the data to the stream....
        repeat
          NumBytes := Buffer.Stream.Write(Buffer.Data[Buffer.DataOffset], Buffer.DataSize-Buffer.DataOffset); 
          if NumBytes <= 0 then
            // stream error, stop reading...
            Socket.Close;
            Exit;
          end;
          Inc(Buffer.DataOffset, NumBytes);
          Dec(Buffer.ExpectedSize, NumBytes);
        until Buffer.DataOffset >= Buffer.DataSize;
      end;
    
      // all done...
      FreeAndNil(Buffer.Stream);
      Buffer.FileName := '';
    end;