2016-10-22 5 views
0

Это следующий вопрос к моему предыдущему вопросу here.Обновление Delphi 7 Indy 9 app to 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, я выполнил поиск и замену chr (166) с помощью ANSIChar (166), но вскоре обнаружил, что Indy 10 не «нравится» ANSIChars выше, чем 127. Запрос выглядит правильно на клиенте, но получен в сервер с? 's

Какой лучший подход к обновлению этого кода? Спасибо.

ответ

1

Indy 10 is UnicodeString -aware, тогда как Indy 9 нет. Delphi 2009 и более поздние версии используют UnicodeString для своего родного типа string, тогда как Delphi 2007 и более ранние версии вместо этого используют AnsiString.

Indy 9 передает AnsiString данные как есть как 8-битные данные. Indy 10 преобразует AnsiString/UnicodeString символов в байты с использованием преобразований кодировки и затем передает байты.

Кодировка Indy 10 по умолчанию - ASCII, где любой символ Юникода выше U + 007F преобразуется в 0x3F. Вы используете символы, превышающие U + 007F, для разделителей параметров, поэтому кодировка ASCII по умолчанию преобразует их в ?, нарушая протокол. Было бы безопаснее использовать вместо этого управляющие символы ASCII < U + 0020, например U + 0001.

Чтобы решить эту проблему без изменения протокола, вы можете установить Indy 10 для использования своей встроенной 8-битной кодировки для строк < -> байтовых конверсий (если вам не нужно отправлять символы Unicode> U + 00FF в вашем протоколе). Чтобы сделать это, вы можете:

  1. установить IOHandler.DefStringEncoding свойство соединения с базой IndyTextEncoding_8Bit после того, как клиент подключается к серверу. Делайте это на обеих сторонах клиента и сервера связи: глобальный GIdDefaultTextEncoding переменной

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

    idTCPClient1.Connect; 
    idTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; 
    
  2. настроенной Инди в 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 (или даже использовать класс Interposer) и переопределить его виртуальный 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; 
    

FYI, на боковой ноте, TCommandHandler имеет ParamDelimiter. Если вы установите его в #166 (это #32 по умолчанию) и установите ParseParams Верно, вы можете удалить AssignDelimited() функции и пусть TIdCommandHandler разобрать ваши разделители параметров в TIdCommand.Params собственности перед обжигом его OnCommand события.

Это может быть даже можно пойти на шаг дальше, создав новый класс от TIdCommandHandler и переопределить его виртуальный DoParseParams() метод для обработки #167 -> CRLF преобразования, а не делать это вручную в каждом обработчике OnCommand событий.

+0

Благодарим за предоставление полного ответа на мои вопросы. – nolaspeaker

Смежные вопросы