将Delphi 7 Indy 9应用程序升级到Indy 10(II)

时间:2016-10-22 17:52:31

标签: delphi indy

这是我之前提问here的后续问题。

许多命令和响应都被编码为分隔字符串。 在Delphi 7中,这些通常使用chr(166)和chr(167)编码。

procedure TFormMain.IdTCPServer1InsertAccount(
  ASender: TIdCommand);
var
  cmd: String;
  request: String;
  Params: TMyStrings;
  AccountNo, Address, UserName: String;
begin
  cmd := 'InsertAccount';
  request := Copy(ASender.Rawline, Length(cmd) + 2, Length(ASender.RawLine));
  Params := TMyStrings.Create;
  try
    AssignDelimited(chr(166), request, Params);
    AccountNo := Params[0];
    Address := replace(char(167), #13#10, Params[1])
    UserName := Params[2];

看来这样做是为了让参数可以包含空格。类似地,内容来自备忘录控件的命令将其回车换行替换为chr(167),因此可以在不终止命令的情况下发送备忘录内容:

// typical client code
request := edAccountNo.Text + chr(166) + 
  replace(#13, chr(167), replace(#10, '', memoAddress.Lines.Text) + 
  chr(166) + Fusername;

idTCPClient1.WriteLn('InsertAccount' + space + request);

现在使用Indy 10将此代码转换为Delphi 10.1,我使用ANSIChar(166)进行了搜索和替换chr(166),但我很快发现Indy 10没有"喜欢& #34; ANSIChars高于127.请求在客户端显示正确但在服务器上收到?'

升级此代码的最佳方法是什么? 感谢。

1 个答案:

答案 0 :(得分:1)

Indy 10是UnicodeString - 意识到,而Indy 9则不是。 Delphi 2009及更高版本使用UnicodeString作为其本机string类型,而Delphi 2007及更早版本使用AnsiString代替。

Indy 9按原样发送AnsiString数据作为8位数据。 Indy 10使用字符集转换将AnsiString / UnicodeString个字符转换为字节,然后传输字节。

Indy 10的默认字符集是ASCII,其中U + 007F以上的任何Unicode字符都将转换为0x3F。您使用大于U + 007F的字符作为参数分隔符,因此默认的ASCII字符集会将它们转换为?,从而破坏您的协议。使用ASCII控制字符<更安全。而是U + 0020,例如U + 0001。

要在不更改协议的情况下解决此问题,您可以将Indy 10设置为使用其内置的8位字符集作为字符串< - >字节转换(只要您不需要在协议中发送Unicode字符> U + 00FF)。为此,您可以:

  1. 在客户端连接到您的服务器后,将连接的IOHandler.DefStringEncoding属性设置为IndyTextEncoding_8Bit。在连接的客户端和服务器端执行此操作:

    procedure TFormMain.IdTCPServer1Connect(AContext: TIdContext);
    begin
      AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
    end;
    

    idTCPClient1.Connect;
    idTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
    
  2. 将Indy在GIdDefaultTextEncoding单元中的全局IdGlobal变量设置为enc8Bit

    procedure TFormMain.FormCreate(Sender: TObject);
    begin
      GIdDefaultTextEncoding := enc8Bit;
    end;
    
  3. 在客户端调用IOHandler.WriteLn()时,您可以在其IndyTextEncoding_8Bit参数中传递AByteEncoding

    idTCPClient1.IOHandler.WriteLn('InsertAccount' + space + request, IndyTextEncoding_8Bit);
    

    在服务器端,分配连接的IOHandler.DefStringEncoding属性是最好的,或者至少设置GIdDefaultTextEncoding变量。但是,作为替代方案,您可以从TIdCmdTCPServer派生一个新组件(甚至使用内插器类)并覆盖其虚拟ReadCommandLine()方法以调用连接的IOHandler.ReadLn()方法,指定{{1在其可选的IndyTextEncoding_8Bit参数中:

    AByteEncoding
  4. 仅供参考,在旁注中,type TIdCmdTCPServer = class(IdCommandHandlers.TIdCmdTCPServer) protected function ReadCommandLine(AContext: TIdContext): string; override; end; TFormMain = class(TForm) IdTCPServer1: TIdCmdTCPServer; ... end; ... function TIdCmdTCPServer.ReadCommandLine(AContext: TIdContext): string; begin Result := AContext.Connection.IOHandler.ReadLn(IndyTextEncoding_8Bit); end; 具有TCommandHandler属性。如果您将其设置为ParamDelimiter(默认情况下为#166)并将#32设置为True,则可以移除ParseParams功能并让AssignDelimited()解析您的TIdCommandHandler在触发TIdCommand.Params事件之前将参数分隔到OnCommand属性中。

    甚至可以通过从TIdCommandHandler派生新类并覆盖其虚拟DoParseParams()方法来处理#167 -> CRLF转换,而不是手动执行此操作,更进一步每个OnCommand事件处理程序。