Indy 10.如何一次发送多个命令?

时间:2018-11-04 08:49:35

标签: delphi indy

告诉我,我的意识形态在这段代码中是不正确的。

我发送一个命令并获得答案,没有任何问题。但是,如果我要(更确切地说,有必要按任务发送)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.

1 个答案:

答案 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;