views:

125

answers:

1

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 initiallised 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);
+2  A: 

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; 
Remy Lebeau - TeamB
sorry i meant if filesize = filestream.position(by file size i mean size to expect)
Omair Iqbal
thanks now i am able to do it, but i do not understand why do i get Exception class EFCreateError with message 'Cannot create file "c:\temp\log.txt". The process cannot access the file because it is being used by another process' when ever i use tfilestream.create method in the ServerSocket1ClientRead event
Omair Iqbal
The error is self explanatory. You are trying to create/open a handle to an existing file that is already in use. `fmCreate` requests exclusive access from the OS, which is failing. `fmCreate` cannot be `or`'ed with any other flags (as it has all available bits set to 1, and is not used as a bit mask by the RTL/VCL anyway), and it cannot be used with other flags in the overloaded TFileStream constructor of newer Delphi versions (at least under Win32).
Remy Lebeau - TeamB
Thanks heaps...!
Omair Iqbal