将Delphi 7 Indy 9应用程序升级到Indy 10(II)
这是对我之前的问题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。请求在客户端显示正确,但是在带有?的服务器
什么是升级此代码的最佳方法? 谢谢。
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在你的协议)。要做到这一点,您可以:
-
设置连接的
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;
-
集Indy的全球
GIdDefaultTextEncoding
变量enc8Bit
:操作上都连接的客户端和服务器端。procedure TFormMain.FormCreate(Sender: TObject); begin GIdDefaultTextEncoding := enc8Bit; end;
-
当调用在客户端
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
事件处理程序。
感谢您始终提供我的问题的完整答案。 – nolaspeaker