这是我之前提问here的后续问题。
许多命令和响应都被编码为分隔字符串。 在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);
现在使用Indy 10将此代码转换为Delphi 10.1,我使用ANSIChar(166)进行了搜索和替换chr(166),但我很快发现Indy 10没有"喜欢& #34; ANSIChars高于127.请求在客户端显示正确但在服务器上收到?'
升级此代码的最佳方法是什么? 感谢。
答案 0 :(得分:1)
Indy 10是UnicodeString
- 意识到,而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;
将Indy在GIdDefaultTextEncoding
单元中的全局IdGlobal
变量设置为enc8Bit
。
procedure TFormMain.FormCreate(Sender: TObject);
begin
GIdDefaultTextEncoding := enc8Bit;
end;
在客户端调用IOHandler.WriteLn()
时,您可以在其IndyTextEncoding_8Bit
参数中传递AByteEncoding
。
idTCPClient1.IOHandler.WriteLn('InsertAccount' + space + request, IndyTextEncoding_8Bit);
在服务器端,分配连接的IOHandler.DefStringEncoding
属性是最好的,或者至少设置GIdDefaultTextEncoding
变量。但是,作为替代方案,您可以从TIdCmdTCPServer
派生一个新组件(甚至使用内插器类)并覆盖其虚拟ReadCommandLine()
方法以调用连接的IOHandler.ReadLn()
方法,指定{{1在其可选的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
设置为True,则可以移除ParseParams
功能并让AssignDelimited()
解析您的TIdCommandHandler
在触发TIdCommand.Params
事件之前将参数分隔到OnCommand
属性中。
甚至可以通过从TIdCommandHandler
派生新类并覆盖其虚拟DoParseParams()
方法来处理#167 -> CRLF
转换,而不是手动执行此操作,更进一步每个OnCommand
事件处理程序。