正在升级 Delphi 7 Indy 9 应用程序。印地 10
Upgrading Delphi 7 Indy 9 app. to Indy 10
我继承了一个广泛的(199 个命令)Delphi 7 Indy 9 应用程序,我正在升级到 Indy 10(在 D10.1 中)。我已经升级了所有代码,它可以编译和运行。我遇到的问题是,现在在 Indy 10 中,除了他们在 Indy 9 下所做的编码响应之外,所有处理程序 也 return 响应代码 (和文本)。 =14=]
例如:
// 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);
有很多命令处理程序,它们都有自己的自定义响应。这是在客户端更改的大量代码。如果命令处理程序已经提供了它自己的响应,有没有办法告诉 IdCmdTCPServer
抑制标准的“200 Ok”响应?还是我熬夜了?
谢谢
如果您需要抑制默认命令响应,您可以:
清除 TIdCommandHandler
的 ReplyNormal
和 ExceptionReply
属性(这在 Indy 9 中也有效,除了 ExceptionReply
是 ReplyExceptionCode
在该版本中),以及服务器的 CommandHandlers.ExceptionReply
属性(仅限 Indy 10)。
在您的 OnCommand
处理程序中将 TIdCommand.PerformReply
属性 设置为 false(这在 Indy 9 中也有效):
procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand);
var
...
begin
ASender.PerformReply := False;
...
end;
将服务器的 CommandHandlers.PerformReplies
属性 设置为 false(仅限 Indy 10 - 默认情况下会将 TIdCommand.PerformReply
设置为 false):
IdCmdTCPServer1.CommandHandlers.PerformReplies := False;
另一方面,您应该考虑按照设计使用的方式使用命令处理程序响应,例如:
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;
我什至会说你应该将 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;
话虽如此,这些方法中的任何一种都可以在不更改现有客户端代码的情况下正常工作。但是,在 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;
或者,您可以让 SendCmd()
在未收到 SUCCESS
回复时引发异常:
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()
确实存在于 Indy 9 中,但它只支持基于数字的响应代码,您没有使用它。如您所见,Indy 10 中的 SendCmd()
支持基于字符串的响应代码以及数字响应代码。
旁注:在您的服务器代码中,OnCommand
处理程序在工作线程中运行,因此您对 ListBox1.Items.AddObject()
的使用不是线程安全的。对 UI 的任何访问都必须 与主 UI 线程同步,使用 TThread.Synchronize()
、TThread.Queue()
、[=43 等技术=]、TIdNotify
等,例如:
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;
确保您的 BillingUserRegistered()
函数同样是线程安全的,如果它还没有的话。
我继承了一个广泛的(199 个命令)Delphi 7 Indy 9 应用程序,我正在升级到 Indy 10(在 D10.1 中)。我已经升级了所有代码,它可以编译和运行。我遇到的问题是,现在在 Indy 10 中,除了他们在 Indy 9 下所做的编码响应之外,所有处理程序 也 return 响应代码 (和文本)。 =14=]
例如:
// 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);
有很多命令处理程序,它们都有自己的自定义响应。这是在客户端更改的大量代码。如果命令处理程序已经提供了它自己的响应,有没有办法告诉 IdCmdTCPServer
抑制标准的“200 Ok”响应?还是我熬夜了?
谢谢
如果您需要抑制默认命令响应,您可以:
清除
TIdCommandHandler
的ReplyNormal
和ExceptionReply
属性(这在 Indy 9 中也有效,除了ExceptionReply
是ReplyExceptionCode
在该版本中),以及服务器的CommandHandlers.ExceptionReply
属性(仅限 Indy 10)。在您的
OnCommand
处理程序中将TIdCommand.PerformReply
属性 设置为 false(这在 Indy 9 中也有效):procedure TFormMain.IdCmdTCPServer1loginCommand(ASender: TIdCommand); var ... begin ASender.PerformReply := False; ... end;
将服务器的
CommandHandlers.PerformReplies
属性 设置为 false(仅限 Indy 10 - 默认情况下会将TIdCommand.PerformReply
设置为 false):IdCmdTCPServer1.CommandHandlers.PerformReplies := False;
另一方面,您应该考虑按照设计使用的方式使用命令处理程序响应,例如:
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;
我什至会说你应该将 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;
话虽如此,这些方法中的任何一种都可以在不更改现有客户端代码的情况下正常工作。但是,在 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;
或者,您可以让 SendCmd()
在未收到 SUCCESS
回复时引发异常:
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()
确实存在于 Indy 9 中,但它只支持基于数字的响应代码,您没有使用它。如您所见,Indy 10 中的 SendCmd()
支持基于字符串的响应代码以及数字响应代码。
旁注:在您的服务器代码中,OnCommand
处理程序在工作线程中运行,因此您对 ListBox1.Items.AddObject()
的使用不是线程安全的。对 UI 的任何访问都必须 与主 UI 线程同步,使用 TThread.Synchronize()
、TThread.Queue()
、[=43 等技术=]、TIdNotify
等,例如:
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;
确保您的 BillingUserRegistered()
函数同样是线程安全的,如果它还没有的话。