[英]Upgrading Delphi 7 Indy 9 app. to Indy 10
I have inherited an extensive (199 commands) Delphi 7 Indy 9 app that I am upgrading to Indy 10 (in D10.1). 我继承了一个广泛的(199个命令)Delphi 7 Indy 9应用程序,该应用程序正在升级到Indy 10(在D10.1中)。 I have upgraded all the code, and it compiles and runs.
我已经升级了所有代码,并且可以编译并运行。 The problem I have is that now in Indy 10 all the handlers also return a response code (and text) in addition to the coded response that they did under Indy 9.
我遇到的问题是,现在在Indy 10中,除了在Indy 9下执行的编码响应之外,所有处理程序还返回响应代码 (和文本)。
For example: 例如:
// server
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
myClient := TClientData.Create;
myClient.ClientName := ASender.Params[0];
myClient.ClientHost := #32; // indy9 was .Thread.Connection.LocalName;
myClient.ID := Now;
ASender.Context.Data := myClient;
ListBox1.Items.AddObject(
PadR(myClient.ClientName,12,' ') + '=' +
FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
TDateTimeO.Create(myClient.ID));
ASender.Context.Connection.IOHandler.WriteLn('SUCCESS' + ' ' + Rights)
end
else
ASender.Context.Connection.IOHander.WriteLn('Login failed!');
end;
... ...
// client side
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
response, response1: String;
begin
frmMain.IdTCPClient1.IOHandler.WriteLn('login' + ' ' +
username + ' ' + password)
response := frmMain.IdTCPClient1.IOHandler.ReadLn();
// I have to add this now to capture the response code too!
response1 := frmMain.IdTCPClient1.IOHandler.ReadLn(); // 200 OK
// ------------------------------------------------------
if Copy(response,1,7) = 'SUCCESS' then
begin
rights := Copy(response,9,4);
There are a lot of command handlers, and they all have their own custom responses. 有很多命令处理程序,它们都有自己的自定义响应。 That's a lot of code to change at the client.
在客户端需要更改很多代码。 Is there a way I can tell the
IdCmdTCPServer
to suppress the standard '200 Ok' response if the command handler already provides it's own? 如果命令处理程序已经提供了自己的命令,是否可以告诉
IdCmdTCPServer
禁止标准的“ 200 OK”响应? Or am I in for a long night? 还是我待了一个晚上?
Thanks 谢谢
If you need to suppress the default command responses, you can either: 如果需要取消默认命令响应,则可以:
clear the TIdCommandHandler
's ReplyNormal
and ExceptionReply
properties (this also works in Indy 9, except that ExceptionReply
was ReplyExceptionCode
in that version), and the server's CommandHandlers.ExceptionReply
property (Indy 10 only). 清除
TIdCommandHandler
的ReplyNormal
和ExceptionReply
属性(这在Indy 9中也适用,除了ExceptionReply
在该版本中是ReplyExceptionCode
),以及服务器的CommandHandlers.ExceptionReply
属性(仅Indy 10)。
set the TIdCommand.PerformReply
property to false in your OnCommand
handler (this also works in Indy 9): 在
OnCommand
处理程序中将TIdCommand.PerformReply
属性设置为false(在Indy 9中也可以使用):
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand); var ... begin ASender.PerformReply := False; ... end;
set the server's CommandHandlers.PerformReplies
property to false (Indy 10 only - it will set TIdCommand.PerformReply
to false by default): 将服务器的
CommandHandlers.PerformReplies
属性设置为false(仅限Indy 10-默认情况下,它将把TIdCommand.PerformReply
设置为false):
IdCmdTCPServer1.CommandHandlers.PerformReplies := False;
On the other hand, you should consider using the command handler responses the way they are designed to be used, eg: 另一方面,您应该考虑使用命令处理程序响应来设计它们的使用方式,例如:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if ASender.Params.Count = 2 then
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
...
ASender.Reply.SetReply('SUCCESS', Rights);
end
else
ASender.Reply.SetReply('ERROR', 'Login failed!');
end
else
ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;
I would even go as far as saying that you should set the TIdCommandHandler.NormalReply.Code
property to SUCCESS
and the TIdCommandHandler.ExceptionReply.Code
property to ERROR
, and then you can do this inside your OnCommand
handler: 我什至可以说应该将
TIdCommandHandler.NormalReply.Code
属性设置为SUCCESS
,并将TIdCommandHandler.ExceptionReply.Code
属性设置为ERROR
,然后可以在OnCommand
处理程序中执行此操作:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
begin
if ASender.Params.Count <> 2 then
raise Exception.Create('Wrong number of parameters!');
if not BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
raise Exception.Create('Login failed!');
...
ASender.Text.Text := Rights;
end;
With that said, any of these approaches should work fine without changing your existing client code. 话虽如此,任何这些方法都可以在不更改现有客户端代码的情况下正常工作。 However, in Indy 10, I would suggest using
SendCmd()
instead of WriteLn()
/ ReadLn()
directly: 但是,在Indy 10中,我建议直接使用
SendCmd()
而不是WriteLn()
/ ReadLn()
:
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
var
response: String;
begin
response := frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password);
if response = 'SUCCESS' then
begin
rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
...
end else begin
// error message in frmMain.IdTCPClient1.LastCmdResult.Text.Text ...
end;
end;
Alternatively, you can let SendCmd()
raise an exception if it does not receive a SUCCESS
reply: 另外,如果未收到
SUCCESS
答复,则可以让SendCmd()
引发异常:
function TfrmLogin.VerifyUserNameAndPassword(username, password: String): Boolean;
begin
try
frmMain.IdTCPClient1.SendCmd('login ' + username + ' ' + password, 'SUCCESS');
except
on E: EIdReplyRFCError do begin
// error message in E.Message ...
...
Exit;
end;
end;
rights := frmMain.IdTCPClient1.LastCmdResult.Text.Text;
...
end;
SendCmd()
does exist in Indy 9, but it only supports numeric-based response codes, which you are not using. Indy 9中确实存在
SendCmd()
,但它仅支持您不使用的基于数字的响应代码。 As you can see above, SendCmd()
in Indy 10 supports string-based response codes as well as numeric ones. 从上面可以看到,Indy 10中的
SendCmd()
支持基于字符串的响应代码以及数字响应代码。
On a side note: in your server code, the OnCommand
handler runs in a worker thread, so your use of ListBox1.Items.AddObject()
is not thread-safe. 附带说明:在服务器代码中,
OnCommand
处理程序在工作线程中运行,因此对ListBox1.Items.AddObject()
使用不是线程安全的。 Any access to the UI must be synchronized with the main UI thread, using techniques like TThread.Synchronize()
, TThread.Queue()
, TIdSync
, TIdNotify
, etc, eg: 对UI的任何访问都必须使用
TThread.Synchronize()
, TThread.Queue()
, TIdSync
, TIdNotify
等技术与主UI线程同步,例如:
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
Rights: String;
myClient: TClientData;
begin
if ASender.Params.Count = 2 then
begin
if BillingUserRegistered(ASender.Params[0], ASender.Params[1], Rights) then
begin
myClient := TClientData(ASender.Context.Data);
if myClient = nil then
begin
myClient := TClientData.Create;
ASender.Context.Data := myClient;
end;
myClient.ID := Now;
myClient.ClientName := ASender.Params[0];
myClient.ClientHost := GStack.HostByAddress(ASender.Context.Binding.PeerIP, ASender.Context.Binding.IPVersion);
// In Indy 9, this would be:
// myClient.ClientHost := GStack.WSGetHostByAddr(ASender.Thread.Connection.Socket.PeerIP);
// NOT ASender.Thread.Connection.LocalName!
TThread.Queue(nil,
procedure
begin
ListBox1.Items.AddObject(
PadR(myClient.ClientName,12,' ') + '=' + FormatDateTime('yyyy-mm-dd hh:nn:ss', myClient.ID),
TDateTimeO.Create(myClient.ID));
end
);
ASender.Reply.SetReply('SUCCESS', Rights);
end
else
ASender.Reply.SetReply('ERROR', 'Login failed!');
end
else
ASender.Reply.SetReply('ERROR', 'Wrong number of parameters!');
end;
Make sure your BillingUserRegistered()
function is similarly thread-safe, if it is not already. 如果还没有,请确保
BillingUserRegistered()
函数同样具有线程安全性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.