升级Delphi 7 Indy 9应用程序。到印地安10号

升级Delphi 7 Indy 9应用程序。到印地安10号,delphi,indy10,Delphi,Indy10,我继承了一个扩展的(199个命令)Delphi7 Indy 9应用程序,我正在升级到Indy 10(在D10.1中)。我已经升级了所有的代码,它编译并运行。我遇到的问题是,现在在Indy 10中,除了在Indy 9中执行的编码响应之外,所有处理程序还返回一个响应代码(和文本) 例如: // server procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand); var Rights: String; begi

我继承了一个扩展的(199个命令)Delphi7 Indy 9应用程序,我正在升级到Indy 10(在D10.1中)。我已经升级了所有的代码,它编译并运行。我遇到的问题是,现在在Indy 10中,除了在Indy 9中执行的编码响应之外,所有处理程序还返回一个响应代码(和文本)

例如:

// 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;

有很多命令处理程序,它们都有自己的自定义响应。在客户端需要更改的代码很多。如果命令处理程序已经提供了标准的“200 Ok”响应,我是否可以告诉
IdCmdTCPServer
抑制该响应?还是我要在这里度过一个漫长的夜晚


谢谢

如果需要抑制默认命令响应,您可以:

  • 清除
    TIdCommandHandler
    ReplyNormal
    ExceptionReply
    属性(这也适用于Indy 9,除了
    ExceptionReply
    在该版本中是
    ReplyExceptionCode
    ),以及服务器的
    commandHandler.ExceptionReply
    属性(仅适用于Indy 10)

  • OnCommand
    处理程序中将
    TIdCommand.PerformReply
    属性设置为false(这也适用于Indy 9):

  • 将服务器的
    CommandHandlers.PerformReplies
    属性设置为false(仅限Indy 10-默认情况下,它将
    TIdCommand.PerformReply
    设置为false):

  • 另一方面,您应该考虑使用命令处理程序响应的方式来设计它们(如:

    )。
    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;
    
    我甚至会说您应该将
    TIdCommandHandler.NormalReply.code
    属性设置为
    SUCCESS
    ,将
    TIdCommandHandler.ExceptionReply.code
    属性设置为
    ERROR
    ,然后您可以在
    OnCommand
    处理程序中执行此操作:

    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;
    
    或者,如果
    SendCmd()
    未收到
    SUCCESS
    回复,则可以让它引发异常:

    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()
    确实存在于Indy 9中,但它只支持基于数字的响应代码,而您不使用这些代码。如上所述,Indy 10中的
    SendCmd()
    支持基于字符串的响应代码以及数字响应代码


    另请注意:在服务器代码中,
    OnCommand
    处理程序在工作线程中运行,因此使用
    ListBox1.Items.AddObject()
    不是线程安全的。任何对UI的访问必须与主UI线程同步,使用诸如
    TThread.Synchronize()
    TThread.Queue()
    TIdSync
    TIdNotify
    等技术,例如:

    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;
    

    确保您的
    BillingUserRegistered()
    函数同样是线程安全的,如果它还不是线程安全的。

    如果您需要抑制默认命令响应,您可以:

  • 清除
    TIdCommandHandler
    ReplyNormal
    ExceptionReply
    属性(这也适用于Indy 9,除了
    ExceptionReply
    在该版本中是
    ReplyExceptionCode
    ),以及服务器的
    commandHandler.ExceptionReply
    属性(仅适用于Indy 10)

  • OnCommand
    处理程序中将
    TIdCommand.PerformReply
    属性设置为false(这也适用于Indy 9):

  • 将服务器的
    CommandHandlers.PerformReplies
    属性设置为false(仅限Indy 10-默认情况下,它将
    TIdCommand.PerformReply
    设置为false):

  • 另一方面,您应该考虑使用命令处理程序响应的方式来设计它们(如:

    )。
    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;
    
    我甚至会说您应该将
    TIdCommandHandler.NormalReply.code
    属性设置为
    SUCCESS
    ,将
    TIdCommandHandler.ExceptionReply.code
    属性设置为
    ERROR
    ,然后您可以在
    OnCommand
    处理程序中执行此操作:

    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;
    
    或者,如果
    SendCmd()
    未收到
    SUCCESS
    回复,则可以让它引发异常:

    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()
    确实存在于Indy 9中,但它只支持基于数字的响应代码,而您不使用这些代码。如上所述,Indy 10中的
    SendCmd()
    支持基于字符串的响应代码以及数字响应代码


    另请注意:在服务器代码中,
    OnCommand
    处理程序在工作线程中运行,因此使用
    ListBox1.Items.AddObject()
    不是线程安全的。任何对UI的访问必须与主UI线程同步,使用诸如
    TThread.Synchronize()
    TThread.Queue()
    TIdSync
    TIdNotify
    等技术,例如:

    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;
    

    确保您的
    BillingUserRegistered()
    函数同样是线程安全的,如果它还没有线程安全的话。

    回答得很好。如果你还没有写一本书,你打算什么时候写?是的,我同意。这是一个非常完整的答案,对此我表示感谢。德尔福社区会发现一本印地指南非常有用。在那之前,我们有这么好的答案。如果你还没有写一本书,你打算什么时候写?是的,我同意。这是一个非常完整的答案,对此我表示感谢。德尔福社区会发现一本印地指南非常有用。在那之前,我们必须这样做。