2016-10-22 150 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