2016-10-22 151 views
0

这是对我之前的问题here的后续问题。将Delphi 7 Indy 9应用程序升级到Indy 10(II)

很多命令和响应被编码为分隔字符串。在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); 

在该代码转换为的Delphi 10.1与现在Indy 10,我用ANSIChar(166)做了一个搜索替换chr(166),但是我很快发现Indy 10不喜欢高于127的ANSIChars。请求在客户端显示正确,但是在带有?的服务器

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

回答

1

Indy 10是UnicodeString-意识到,而Indy 9不是。德尔福2009和更高版本使用UnicodeString为其原生string类型,而德尔福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客户端连接到服务器后。在IdGlobal单元

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

    idTCPClient1.Connect; 
    idTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; 
    
  2. 集Indy的全球GIdDefaultTextEncoding变量enc8Bit:操作上都连接的客户端和服务器端。

    procedure TFormMain.FormCreate(Sender: TObject); 
    begin 
        GIdDefaultTextEncoding := enc8Bit; 
    end; 
    
  3. 当调用在客户端IOHandler.WriteLn(),你可以在它的可选AByteEncoding参数传递IndyTextEncoding_8Bit

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

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

    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)并将ParseParams设置为True,则可以删除AssignDelimited()函数,并在触发其OnCommand事件之前让TIdCommandHandler解析您的分隔参数到TIdCommand.Params属性中。

它甚至有可能走了一步,从TIdCommandHandler派生新类并覆盖其虚拟DoParseParams()方法来处理#167 -> CRLF转换,而不用手动做,在每个OnCommand事件处理程序。

+0

感谢您始终提供我的问题的完整答案。 – nolaspeaker