将 Delphi 7 Indy 9 应用程序升级到 Indy 10 (II)
Upgrading 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];
这样做似乎是为了让参数可以包含空格。类似地,内容来自备忘录控件的命令将其回车 return 换行符替换为 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 没有 "like" ANSIChars 高于 127。请求在客户端看起来是正确的,但在服务器端收到时带有 ?'s
升级此代码的最佳方法是什么?
谢谢
Indy 10 是 UnicodeString
-aware,而 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 在你的协议中)。为此,您可以:
在客户端连接到您的服务器后,将连接的 IOHandler.DefStringEncoding
属性 设置为 IndyTextEncoding_8Bit
。在连接的客户端和服务器端执行此操作:
procedure TFormMain.IdTCPServer1Connect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
end;
idTCPClient1.Connect;
idTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
将IdGlobal
单元中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
派生一个新组件(或者甚至使用插入器 class)并覆盖其虚拟 ReadCommandLine()
方法以调用连接的 IOHandler.ReadLn()
方法在其可选的 AByteEncoding
参数中指定 IndyTextEncoding_8Bit
:
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()
函数并让 TIdCommandHandler
解析您的在触发其 OnCommand
事件之前将分隔参数放入 TIdCommand.Params
属性。
甚至可以更进一步,从 TIdCommandHandler
派生新的 class 并覆盖其虚拟 DoParseParams()
方法来处理 #167 -> CRLF
转换,而不是在每个 OnCommand
事件处理程序中手动执行此操作。
这是我之前问题
很多命令和响应都被编码为分隔字符串。 在 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];
这样做似乎是为了让参数可以包含空格。类似地,内容来自备忘录控件的命令将其回车 return 换行符替换为 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 没有 "like" ANSIChars 高于 127。请求在客户端看起来是正确的,但在服务器端收到时带有 ?'s
升级此代码的最佳方法是什么? 谢谢
Indy 10 是 UnicodeString
-aware,而 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 在你的协议中)。为此,您可以:
在客户端连接到您的服务器后,将连接的
IOHandler.DefStringEncoding
属性 设置为IndyTextEncoding_8Bit
。在连接的客户端和服务器端执行此操作:procedure TFormMain.IdTCPServer1Connect(AContext: TIdContext); begin AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit; end;
idTCPClient1.Connect; idTCPClient1.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
将
IdGlobal
单元中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
派生一个新组件(或者甚至使用插入器 class)并覆盖其虚拟ReadCommandLine()
方法以调用连接的IOHandler.ReadLn()
方法在其可选的AByteEncoding
参数中指定IndyTextEncoding_8Bit
: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()
函数并让 TIdCommandHandler
解析您的在触发其 OnCommand
事件之前将分隔参数放入 TIdCommand.Params
属性。
甚至可以更进一步,从 TIdCommandHandler
派生新的 class 并覆盖其虚拟 DoParseParams()
方法来处理 #167 -> CRLF
转换,而不是在每个 OnCommand
事件处理程序中手动执行此操作。