如果您需要抑制默认命令响应,您可以:
清除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()、TIdSync、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() 函数同样是线程安全的(如果还不是的话)。