使用TIdNotify.NotifyMethod时会丢弃某些数据包

时间:2013-08-17 09:16:56

标签: delphi indy

我有一台服务器每0.1秒发送一次状态。我使用此代码访问UI并在客户端程序上显示结果。

procedure TModules.TCPServerExecute(AContext: TIdContext);
begin
   Buffer:= AContext.Connection.IOHandler.ReadLn();
   TIdNotify.NotifyMethod( ServerExecute );
end;

ServerExecute未收到某些数据包。我更改了代码并使用了TIdSync.SynchronizeMethod。问题解决了:

procedure TModules.TCPServerExecute(AContext: TIdContext);
begin
   Buffer:= AContext.Connection.IOHandler.ReadLn();
   TIdSync.SynchronizeMethod( ServerExecute );
end;

我在这个网站上看到TIdSync.SynchronizeMethod可能会产生死锁。 所以我想知道TIdNotify.NotifyMethod的问题是什么。另外,我看到一个答案建议使用Timer同步UI,不使用Notify()和Synchronize()方法。

另外,我必须提到该程序在ServerExecute内完成其余的工作。

如您所见,我正在将代码缩小到以下内容。我必须提一下,我使用以下代码执行该程序,它已经存在问题并且没有得到预期的结果:

procedure TModules.ServerExecute;
var
  I: Word;
  tmp, Packet, Cmd:string;
  CheckSum: Boolean;
begin
  //try
    frmDiag.DataLog.Lines.Add(Buffer);

    {ExplodeStr(Buffer, Cmd, Packet);

    if Cmd='status' then
    begin

      //ExplodeStr(Packet, Cmd, Packet);

      if trim(Packet)='' then
        raise Exception.Create('Empty packet received.');

      //Prepare for log
      {tmp:='';
      for I := 1 to Length(Packet) do
      begin
        if (Ord(Packet[I])>=48) and (Ord(Packet[I])<=122) then
          tmp:=tmp+Packet[I]+''
        else
          tmp:=tmp+IntToStr(Ord(Packet[I]))+'';
      end;

      //frmITCAMain.Memo1.Lines.Add(Packet);
      CheckSum:=ParsePackets(Packet);
      IntersectionsList.Int.CallNotifier;       //Call the notifier to execute assigned proc

      if frmLoader.Visible=true then
        with frmLoader do
        begin
          if
            //(Packet[1]='X') or            //Server responce
            (Packet[1]<>'l') and (Packet[1]<>'s') and (Packet[1]<>'e')and   //ignore general packet
            (
                (Req[1]<>'f')                       //Only receive ACK
                or
                ((Req[1]='f')and( (Req[2]='m')or(Req[2]='a')or(Req[2]='b') ))
                or
                (                       //Receive Data+ACK
                  (Req[1]='f')and( (Req[2]='g')or(Req[2]='h')or(Req[2]='p')or(Req[2]='j') )
                  and
                  (Packet[1]<>'k')      //Ignore ACK
                )
            )
          then
            begin
              if CheckSum then
              begin
                Res:= Packet;
                Confirm;
              end
              else
              begin
                if Packet='n' then  //Checksum failure
                  Fail
                else
                  SendPacket;   //Resend.
                //lblError.Visible:=true;
              end;
            end;
        end;

      if (Packet[1]='g') or (Packet[1]='h') or (Packet[1]='p') or
      (Packet[1]='j')  or (Packet[1]='k') then
          frmIntDetails.memReceived.Lines.Text:=tmp;

    end

    else if Cmd='server' then
    begin
      with frmLoader do
      begin
          if Visible=false then exit;

        Res:= Packet;
        if Copy(Res, 1, 2)='ok' then
            Confirm
        else
            Cancel;
      end;
    end

    else
      ClientLog.Add(Buffer);

  except on E: Exception do
    begin
      if E.Message='Connection Closed Gracefully.' then
        ClientLog.Add('X:Connection closed(Gracefully)')
      else
        ClientLog.Add(E.Message+' Buffer="'+Buffer+'"');
    end;
  end;
  //Memo2.Lines.Add( FloatToStr(TimeStampToMSecs(DateTimeToTimeStamp(Now))));
  }
end;

frmDiag.DataLogTMemo个组件。

frmDiag.DataLog内部,例如,以下列表是我期望的结果(以下字符串是从带有TIdSync.SynchronizeMethod解决方案的Datalog组件中提取的):

status:l77770000140000
status:eFFFF20000140
status:s0000
status:s0000
status:s0000
status:s0000
status:l00005555140000
status:eFFFF20000140
status:s0000
status:s0000
status:s0000
status:s0000
status:l77770000140000
status:eFFFF20000140

但我得到了这个结果:

status:eFFFF20000140
status:eFFFF20000140    
status:s0000
status:s0000
status:s0000
status:s0000
status:s0000
status:s0000
status:s0000
status:s0000
status:s0000
status:l00005555140000
status:l77770000140000  

如您所见订单不符合。

我必须获得一个status:l77770000140000类似的数据包,然后是status:eFFFF20000140,然后是4 status:s0000,依此类推。

@Remy我已经改变了你的代码:

TMyNotify = class(TIdNotify)
protected
  FBuffer: String;
  FMethod: TThreadMethod;
  procedure DoNotify; override;
public
  constructor Create(const ABuffer: String; AMethod: TThreadMethod);
end;

///......

{ TMyNotify }

constructor TMyNotify.Create(const ABuffer: String; AMethod: TThreadMethod);
begin
  inherited Create;
  FBuffer := ABuffer;
  FMethod := AMethod;
end;

procedure TMyNotify.DoNotify;
begin
  inherited;
  FMethod;
  //Destroy;
end;

这就是我现在调用ServerExcecute的方式:

procedure TModules.TCPServerExecute(AContext: TIdContext);
begin
  Buffer:= AContext.Connection.IOHandler.ReadLn();

  TMyNotify.Create(Buffer, ServerExecute).Notify;

//  ServerExecute;
//  TIdNotify.NotifyMethod( ServerExecute );
//  TIdSync.SynchronizeMethod( ServerExecute );
end;

1 个答案:

答案 0 :(得分:4)

TIdNotify是异步的。如果您收到新行并在上一行有机会被处理之前覆盖您的Buffer变量,则前一行将丢失。更改代码以传递TIdNotify本身内的行值,例如:

type
  TMyDataProc = procedure(const ABuffer: String) of object;

  TMyNotify = class(TIdNotify)
  protected
    fBuffer: String;
    fProc: TMyDataProc;
    procedure DoNotify; override;
  public
    constructor Create(const ABuffer: String; AProc: TMyDataProc);
  end;

constructor TMyNotify.Create(const ABuffer: String; AProc: TMyDataProc);
begin
  inherited Create;
  fBuffer := ABuffer;
  fProc := AProc;
end;

procedure TMyNotify.DoNotify;
begin
  fProc(fBuffer);
end;

procedure TModules.TCPServerExecute(AContext: TIdContext);
var
  LBuffer: String;
begin
  LBuffer := AContext.Connection.IOHandler.ReadLn();
  TMyNotify.Create(LBuffer, ServerExecute).Notify();
end;

procedure TModules.ServerExecute(const ABuffer: String);
begin
  // use ABuffer as needed...
end;