Upgrading Delphi 7 Indy 9 app. to Indy 10

大兔子大兔子 提交于 2020-01-07 02:56:23

问题


I have inherited an extensive (199 commands) Delphi 7 Indy 9 app that I am upgrading to Indy 10 (in D10.1). I have upgraded all the code, and it compiles and runs. The problem I have is that now in Indy 10 all the handlers also return a response code (and text) in addition to the coded response that they did under Indy 9.

For example:

// server 
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
  Rights: String;
begin

   if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
   begin
      myClient := TClientData.Create;
      myClient.ClientName := ASender.Params[0];
      myClient.ClientHost := #32; // indy9 was .Thread.Connection.LocalName; 
      myClient.ID := Now;
      ASender.Context.Data := myClient;

      ListBox1.Items.AddObject(
        PadR(myClient.ClientName,12,' ') + '=' +
        FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
        TDateTimeO.Create(myClient.ID));
      ASender.Context.Connection.IOHandler.WriteLn('SUCCESS' + ' ' + Rights)  
  end
  else
      ASender.Context.Connection.IOHander.WriteLn('Login failed!');

end;

...

// client side
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
  response, response1: String;
begin

  frmMain.IdTCPClient1.IOHandler.WriteLn('login' + ' ' + 
     username + ' ' + password)
  response := frmMain.IdTCPClient1.IOHandler.ReadLn();
  // I have to add this now to capture the response code too!
  response1 := frmMain.IdTCPClient1.IOHandler.ReadLn(); // 200 OK
  // ------------------------------------------------------
  if Copy(response,1,7) = 'SUCCESS' then
  begin
    rights := Copy(response,9,4);

There are a lot of command handlers, and they all have their own custom responses. That's a lot of code to change at the client. Is there a way I can tell the IdCmdTCPServer to suppress the standard '200 Ok' response if the command handler already provides it's own? Or am I in for a long night?

Thanks


回答1:


If you need to suppress the default command responses, you can either:

  1. clear the TIdCommandHandler's ReplyNormal and ExceptionReply properties (this also works in Indy 9, except that ExceptionReply was ReplyExceptionCode in that version), and the server's CommandHandlers.ExceptionReply property (Indy 10 only).

  2. set the TIdCommand.PerformReply property to false in your OnCommand handler (this also works in Indy 9):

    procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
    var
      ...
    begin
      ASender.PerformReply := False;
      ...
    end;
    
  3. set the server's CommandHandlers.PerformReplies property to false (Indy 10 only - it will set TIdCommand.PerformReply to false by default):

    IdCmdTCPServer1.CommandHandlers.PerformReplies := False;
    

On the other hand, you should consider using the command handler responses the way they are designed to be used, eg:

procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
  Rights: String;
begin
  if ASender.Params.Count = 2 then
  begin
    if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
    begin
      ...
      ASender.Reply.SetReply('SUCCESS', Rights);
    end
    else
      ASender.Reply.SetReply('ERROR', 'Login failed!');
  end
  else
    ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;

I would even go as far as saying that you should set the TIdCommandHandler.NormalReply.Code property to SUCCESS and the TIdCommandHandler.ExceptionReply.Code property to ERROR, and then you can do this inside your OnCommand handler:

procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
  Rights: String;
begin
  if ASender.Params.Count <> 2 then
    raise Exception.Create('Wrong number of parameters!');

  if not BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
    raise Exception.Create('Login failed!');

  ...

  ASender.Text.Text := Rights;
end;

With that said, any of these approaches should work fine without changing your existing client code. However, in Indy 10, I would suggest using SendCmd() instead of WriteLn()/ReadLn() directly:

function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
  response: String;
begin
  response := frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password);
  if response = 'SUCCESS' then
  begin
    rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
    ...
  end else begin
    // error message in frmMain.IdTCPClient1.LastCmdResult.Text.Text ...
  end;
end;

Alternatively, you can let SendCmd() raise an exception if it does not receive a SUCCESS reply:

function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
begin
  try
    frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password, 'SUCCESS');
  except
    on E: EIdReplyRFCError do begin
      // error message in E.Message ...
      ...
      Exit;
    end;
  end;

  rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
  ...
end;

SendCmd() does exist in Indy 9, but it only supports numeric-based response codes, which you are not using. As you can see above, SendCmd() in Indy 10 supports string-based response codes as well as numeric ones.


On a side note: in your server code, the OnCommand handler runs in a worker thread, so your use of ListBox1.Items.AddObject() is not thread-safe. Any access to the UI must be synchronized with the main UI thread, using techniques like TThread.Synchronize(), TThread.Queue(), TIdSync, TIdNotify, etc, eg:

procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
  Rights: String;
  myClient: TClientData;
begin
  if ASender.Params.Count = 2 then
  begin
    if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
    begin
      myClient := TClientData(ASender.Context.Data);
      if myClient = nil then
      begin
        myClient := TClientData.Create;
        ASender.Context.Data := myClient;
      end;

      myClient.ID := Now;
      myClient.ClientName := ASender.Params[0];

      myClient.ClientHost := GStack.HostByAddress(ASender.Context.Binding.PeerIP, ASender.Context.Binding.IPVersion);
      // In Indy 9, this would be:
      // myClient.ClientHost := GStack.WSGetHostByAddr(ASender.Thread.Connection.Socket.PeerIP);
      // NOT ASender.Thread.Connection.LocalName!

      TThread.Queue(nil,
        procedure
        begin
          ListBox1.Items.AddObject(
            PadR(myClient.ClientName,12,' ') + '=' + FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
            TDateTimeO.Create(myClient.ID));
        end
      );

      ASender.Reply.SetReply('SUCCESS', Rights);
    end
    else
      ASender.Reply.SetReply('ERROR', 'Login failed!');
  end
  else
    ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;

Make sure your BillingUserRegistered() function is similarly thread-safe, if it is not already.



来源:https://stackoverflow.com/questions/40114391/upgrading-delphi-7-indy-9-app-to-indy-10

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!