2

我有一个服务器每 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。我还看到一个建议使用 aTimer来同步 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.DataLog是一个TMemo组件。

例如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;
4

1 回答 1

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;
于 2013-08-17T16:26:38.947 回答