2
\$\begingroup\$

I'm a newbie in Delphi and I'm trying to create a client/server application using INDY. I want to have a server that is capable not only of receiving messages and streaming, but also sending data to clients at any given time. The clients must have same capabilities.

I built a prototype app using TIdTCPServer and TIdTCPClient and it's working within my test environment, but since I'm new on Delphi and have no prior experience with TCP programming, I'm not sure if this is the best way of doing it.

The communication between server and client occurs when someone (server or clients) sends a command (string) to the other side.

If the command is to send a file then first the send_file string is sent and then a stream containing the file.
For the server to receive a file, it instructs the client (via string command) to ask for the file: server sends rcv_file to client which reads the command and sends a send_file string command to server and then the actual file via streaming right after.

Server

//==============================================================================
// Server Execute procedure
//==============================================================================
procedure TfrmMain.TCPServerExecute(AContext: TIdContext);
var
  Cmd: string;
  Date: TDateTime;
  Timestamp: string;
  Stream: TMemoryStream;
begin
  Cmd := AContext.Connection.IOHandler.ReadLn;

  Date := Now;
  Timestamp := FormatDateTime('yyyymmdd_hhmmss', Date);

  if Cmd = 'send_file' then
  begin
    try
      Stream := TMemoryStream.Create;

      try
        AContext.Connection.IOHandler.ReadStream(Stream, -1, False);

        Stream.Position := 0;

        Stream.SaveToFile(ExtractFilePath(Application.ExeName) +
          '\recv_test' + Timestamp + '.dat');
      except on E: Exception do
        ShowMessage('Error loading file: ' + E.ClassName + ' - ' + E.Message);
      end;
    finally
      Stream.Free;
    end;
  end else if Cmd = 'recv_file' then
  begin
    try
      Stream := TMemoryStream.Create;

      try
        Stream.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.dat');
        Stream.Position := 0;

        AContext.Connection.IOHandler.WriteLn('send_file');
        AContext.Connection.IOHandler.Write(Stream, 0, True);
      except on E: Exception do
        ShowMessage('Error sending file: ' + E.ClassName + ' - ' + E.Message);
      end;
    finally
      Stream.Free;
    end;
  end;
end;
//==============================================================================
// Server send file button
//==============================================================================
procedure TfrmMain.btnSendFileClick(Sender: TObject);
var
  List: TList;
  ip: string;
  I: Integer;
  Context: TIdContext;
  Stream: TMemoryStream;
begin
  // lvwPCList is a ListView on my form...
  // Do I need to use TThread.Queue to safety access this component?
  ip := GStack.ResolveHost(lvwPCList.Selected.Caption);

  try
    List := TCPServer.Contexts.LockList;

    for I := 0 to List.Count - 1 do
    begin
    Context := TIdContext(List[I]);

      if Context.Connection.Socket.Binding.PeerIP = ip then
      begin
      Context.Connection.IOHandler.WriteLn('send_file');

        try
          Stream := TMemoryStream.Create;

          try
            Stream.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.dat');
            Stream.Position := 0;

            Context.Connection.IOHandler.Write(Stream, 0, True);
          except on E: Exception do
            ShowMessage('Error sending file: ' + E.ClassName + ' - ' + E.Message);
          end;
        finally
          Stream.Free;
        end;

        Break;
      end;
    end;
  finally
    TCPServer.Contexts.UnlockList;
  end;
end;
//==============================================================================
//  Server get file button
//==============================================================================
procedure TfrmMain.btnGetFileClick(Sender: TObject);
var
  List: TList;
  IP: string;
  I: Integer;
  Context: TIdContext;
begin
  IP := GStack.ResolveHost(lvwPCList.Selected.Caption);

  try
    List := TCPServer.Contexts.LockList;

    for I := 0 to List.Count - 1 do
    begin
    Context := TIdContext(List[I]);

      if Context.Connection.Socket.Binding.PeerIP = IP then
      begin
        Context.Connection.IOHandler.WriteLn('recv_file');
        // After send the string command 'recv_file', the server
        // will receive the actual file sending by client on the execute event...
        Break;
      end;
    end;
  finally
    TCPServer.Contexts.UnlockList;
  end;
end;
//==============================================================================

Client

//==============================================================================
procedure ThreadActionStart;
var
  ThreadAction: TThreadAction;
begin
  ThreadAction := TThreadAction.Create(True);
  ThreadAction.FreeOnTerminate := True;
  ThreadAction.Priority := tpNormal;
  ThreadAction.Start;
end;
//==============================================================================
procedure TThreadAction.Execute;
var
  Cmd: string;
  Date: TDateTime;
  Timestamp: string;
  Stream: TMemoryStream;
begin
  while frmMain.IdTCPClient.Connected do
  begin
    try
      cmd := frmMain.IdTCPClient.IOHandler.ReadLn;

    TThread.Queue( nil,
      procedure
    begin
      frmMain.lstMessages.Items.Add(cmd);
    end
    );

      Date := Now;
      Timestamp := FormatDateTime('yyyymmdd_hhmmss', Date);

      if Cmd = 'send_file' then
      begin
        try
          Stream := TMemoryStream.Create;

          try
            frmMain.IdTCPClient.IOHandler.ReadStream(Stream, -1);
            Stream.Position := 0;
            Stream.SaveToFile(ExtractFilePath(Application.ExeName) +
              '\recv_test' + Timestamp + '.dat');
          except on E: Exception do
            ShowMessage('Error loading file: ' + E.ClassName + ' - ' + E.Message);
          end;
        finally
          Stream.Free;
        end;
      end else if Cmd = 'recv_file' then
      begin
        try
          Stream := TMemoryStream.Create;

          try
            Stream.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.dat');
            Stream.Position := 0;

            frmMain.IdTCPClient.IOHandler.WriteLn('send_file');
            frmMain.IdTCPClient.IOHandler.Write(Stream, 0, True);
          except on E: Exception do
            ShowMessage('Error sending file: ' + E.ClassName + ' - ' + E.Message);
          end;
        finally
          Stream.Free;
        end;
      end;
    except
      ShowMessage('Error reading from server');
    end;
  end;
end;
//==============================================================================
procedure TfrmMain.btnGetFileClick(Sender: TObject);
begin
  frmMain.IdTCPClient.IOHandler.WriteLn('recv_file');
end;
//==============================================================================
procedure TfrmMain.btnSendFileClick(Sender: TObject);
var
  Stream: TMemoryStream;
begin
  try
    frmMain.IdTCPClient.IOHandler.WriteLn('send_file');

    Stream := TMemoryStream.Create;

    try
      Stream.LoadFromFile(ExtractFilePath(Application.ExeName) + 'test.dat');
      Stream.Position := 0;

      frmMain.IdTCPClient.IOHandler.Write(Stream, 0, True);
    except on E: Exception do
      ShowMessage('Error sending file: ' + E.ClassName + ' - ' + E.Message);
    end;
  finally
      Stream.Free;
  end;
end;
//==============================================================================
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  try
    IdTCPClient.Host := '192.168.0.20';
    IdTCPClient.Port := 4545;
    IdTCPClient.Connect;
  except
    ShowMessage('Connection error');
  end;
end;
//==============================================================================
procedure TfrmMain.IdTCPClientConnected(Sender: TObject);
begin
  try
    ThreadActionStart;
  except
    ShowMessage('Error - Thread not started');
  end;
end;
//==============================================================================

I would like to know if I'm on the right way, any advice or tips will be helpful.

\$\endgroup\$
  • \$\begingroup\$ This question was answered on StackOverflow link. Thanks anyway! \$\endgroup\$ – SkynetAdmin Dec 31 '14 at 15:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.