告诉我,我的意识形态在这段代码中是不正确的。
我发送一个命令并获得答案,没有任何问题。但是,如果我要(更确切地说,有必要按任务发送)N条命令并正确接收所有答案。
如何正确发送一个命令,等待上一个命令完全完成。
发送几个命令,失去连接,但有时会跳过,更改CheckForDataOnSource(100); -没有帮助。
今天我有以下代码:
ButtonClick(Sender: TObject);
var
MyServ: TMyServer;
begin
...
MyServ.SendCommand('command 1');
...
MyServ.SendCommand('command N');
end;
unit MyServer;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, Vcl.StdCtrls, IdGlobal,
XMLDoc, XMLDOM, XMLIntf, ActiveX, Vcl.ExtCtrls, Vcl.ComCtrls, Base64, IdSync, IdCmdTCPServer,
IdYarn, IdTCPConnection, IdThreadSafe;
type
TMyServer = class(TIdTCPServer)
private
FEndResponse: string;
FTotalStr: string;
procedure Execute(AContext: TIdContext);
//procedure ParseRecv(RecvText: string);
public
procedure Run(BindIP: string; BindPort: Integer);
procedure Stop;
procedure SendCommand(Cmd: string);
constructor Create(AOwner: TComponent);
destructor Destroy; override;
end;
TMySync = class(TIdSync)
protected
procedure DoSynchronize; override;
public
Data: string;
Server: TMyServer;
end;
TMyContext = class(TIdServerContext)
public
Queue: TIdThreadSafeStringList;
constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
destructor Destroy; override;
end;
var
FReady: Boolean = True;
implementation
uses Main;
procedure TMySync.DoSynchronize;
begin
//Server.ParseRecv(Data);
MainForm.Memo1.Lines.Add(Data);
end;
constructor TMyServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ContextClass := TMyContext;
OnExecute := Execute;
FEndResponse := '</response>';
end;
destructor TMyServer.Destroy;
begin
Stop;
inherited Destroy;
end;
constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
inherited Create(AConnection, AYarn, AList);
Queue := TIdThreadSafeStringList.Create;
end;
destructor TMyContext.Destroy;
begin
Queue.Free;
inherited Destroy;
end;
procedure TMyServer.Run(BindIP: string; BindPort: Integer);
begin
DefaultPort := 0;
Bindings.Clear;
with Bindings.Add do begin
IP := BindIP;
Port := BindPort;
end;
Active := True;
end;
procedure TMyServer.Stop;
begin
Active := False;
end;
procedure TMyServer.SendCommand(Cmd: string);
var
List: TList;
Ctx: TMyContext;
begin
List := Contexts.LockList;
try
//Connection Server <-> Client once one to one
Ctx := TMyContext(List[0]);
Ctx.Queue.Add(Cmd);
finally
Contexts.UnlockList;
end;
end;
procedure TMyServer.Execute(AContext: TIdContext);
var
Buffer: TIdBytes;
Size, RecvSizeLen: Integer;
RecvStr: string;
Sync: TMySync;
Ctx: TMyContext;
Queue: TStringList;
begin
Buffer := nil;
with AContext.Connection.IOHandler do begin
CheckForDataOnSource(100);
if not InputBufferIsEmpty then begin
Size := StrToInt(ReadLn());
InputBuffer.ExtractToBytes(Buffer);
SetString(RecvStr, PAnsiChar(@Buffer[0]), Size);
FTotalStr := FTotalStr + RecvStr;
if ((Copy(FTotalStr, Length(FTotalStr) - Length(FEndResponse), Length(FEndResponse))) = FEndResponse)
then begin
if Length(FTotalStr) > 0 then begin
Sync := TMySync.Create;
try
Sync.Server := Self;
Sync.Data := FTotalStr;
Sync.Synchronize;
finally
Sync.Free;
end;
end;
FTotalStr := '';
end;
end else begin
Ctx := TMyContext(AContext);
Queue := Ctx.Queue.Lock;
//????????????????????????????????
while (Queue.Count > 0) do begin
try
Ctx.Connection.IOHandler.Write(Queue[0] + #0);
Queue.Delete(0);
finally
Ctx.Queue.Unlock;
end;
end;
end;
end;
end;
end.
答案 0 :(得分:1)
我发现您的Execute
代码有很多问题。
在阅读答复时,您首先从连接中读取了一个大小,但随后在调用ExtractToBytes()
时忽略了该大小,因此最终返回所有可用字节,然后使用该大小进行复制将提取的字节的一部分放入字符串中,然后丢弃其余字节。因此,此逻辑可能会丢弃协议中的字节并破坏通信。您根本不应该这样使用ExtractToBytes()
,而应使用IOHandler的ReadBytes()
方法,或者也许使用ReadString()
,这取决于所使用的Delphi版本和字符串的性质正在阅读。
在检查队列中是否有要发送的数据时,可以锁定队列,但是如果队列为空,则不要解锁。如果不为空,则在每次循环迭代结束时都将其解锁,而无需先重新锁定它。您的try..finally
需要移到while
循环之外。
尝试以下方法:
// if you are using D2009+, add this...
uses
..., System.AnsiStrings;
procedure TMyServer.Execute(AContext: TIdContext);
var
IO: TIdIOHandler;
Buffer: TIdBytes;
Size: Integer;
RecvStr: AnsiString;
Sync: TMySync;
Ctx: TMyContext;
Queue: TStringList;
begin
IO := AContext.Connection.IOHandler;
if IO.InputBufferIsEmpty then
begin
IO.CheckForDataOnSource(100);
IO.CheckForDisconnect;
end;
if not IO.InputBufferIsEmpty then
begin
Size := StrToInt(IO.ReadLn());
// if using D2007 or earlier...
RecvStr := IO.ReadString(Size);
// if using D2009 or later...
IO.ReadBytes(Buffer, Size, False);
SetString(RecvStr, PAnsiChar(Pointer(Buffer)), Size);
FTotalStr := FTotalStr + RecvStr;
if AnsiEndsStr(FEndResponse, FTotalStr) then
begin
Sync := TMySync.Create;
try
Sync.Server := Self;
Sync.Data := FTotalStr;
Sync.Synchronize;
finally
Sync.Free;
end;
FTotalStr := '';
end;
end else
begin
Ctx := TMyContext(AContext);
Queue := Ctx.Queue.Lock;
try
while (Queue.Count > 0) do
begin
IO.Write(Queue[0] + #0);
Queue.Delete(0);
end;
finally
Ctx.Queue.Unlock;
end;
end;
end;