Потерянные данные с Indy10 и iDTCPServer и idTCPClient - PullRequest
0 голосов
/ 08 ноября 2019

У меня есть две программы, использующие Delphy 10.3 и Indy 10 idTCPServer и idTCPClient0:

Сторона сервера:

  • Сервер регулярно отправляет набор данных 1 из TIDBytes, используя таймер (150байт данных)
  • Сервер отправляет набор данных 2 из TIDBytes, но только время от времени
  • Сервер отправляет данные с использованием TIDServer.Connection.IOHandler.Write (IDBytesArray);

Клиентская сторона:

  • Клиент получает TIDBytes с потоком, как описано во многих примерах

Проблема

  • Если таймер сервера отправляет данные с низкой частотой (1000 миллисекунд), то все работает нормально, клиент получает оба набора данных (один из таймера сервера, а другой часто отправляемый).
  • НО, если серверный таймер отправляет набор данных 1 с высокой частотой (каждые 50 миллисекунд), не все данные из второго набора данных могут быть получены (но все данные, отправленные таймером, принимаются).

Вопрос - может ли это быть проблемой с буфером? Или таймер перезаписывает набор данных 2, который уже находится в буфере, но еще не прочитан клиентом? У меня есть какой-нибудь способ преодолеть эту ситуацию?

1 Ответ

0 голосов
/ 11 ноября 2019

Лебо, спасибо тебе, действительно, за ответ. Я думаю, вы правильно упомянули, что таймер может отправлять данные в порт, и это может испортить другой таймер, который одновременно записывает данные в порт. Поэтому я сократил пример:

  • Клиент регулярно отправляет на сервер записи структуры Data1 в виде tidBytes.
  • Сервер отвечает подтверждением.
  • Клиент проверяет, относится ли подтверждение к последнему отправленному набору данных. Это прекрасно работает , пока клиент не отправляет пакеты быстрее, чем каждые 100 миллисекунд . Если отправка скажет aevry 10 миллисекунд, то некоторые подтверждения будут потеряны на стороне клиента. Если бы это помогло, я мог бы поделиться всем демо-проектом. Но, может быть, это не то, для чего сделан стековый поток. Поэтому я добавляю немного кода ниже:

Определены две записи данных: - TData1 - это та, которая отправляется с клиента на сервер. - Сервер отправляет TAcknowledgeData1 обратно клиенту в качестве подтверждения

type
  TData1 = record                         // 11 bytes record
   {header:}
    MessageStart  : Array[1..4] of Char;  // 0
    MessageID     : word;                 // 4
   {content:}
    TransactionNo : Integer;              // 6
    TrackerMsgID  : byte;                 // 10
  end;


type
  TAcknowledgeData1 = record              // 14 bytes record
   {Header:}
    MessageStart  : Array[1..4] of Char;  // 0
    MessageID     : Word;                 // 4
   {content:}
    TransactionNo : Integer;              // 6
    EventID       : Integer;              // 10
 end;

У клиента есть таймер, который регулярно отправляет набор данных1 на сервер, вызывающий процедуру SendData1:

procedure TForm1.TimerClientTimer(Sender: TObject);
begin
  SendData1(TransactionNr);
  TransactionNr := TransactionNr + 1;
end;

procedure TForm1.SendData1 (Transaction: Integer);
var Data1      : TData1;
    Data1Array : TIDBytes;
begin

   {fill the data1 record:}
    Data1.MessageStart := '$MSG';       //4 bytes
    Data1.MessageID    := 1;            //2 bytes
    Data1.TransactionNo:= Transaction;  //4 bytes
    Data1.TrackerMsgID := 1;            //1 byte
   {set the length of idBytes to get the dta 1 record:}
    setLength (Data1Array,11);
   {move data1 record to idBytes array:}
    Move (Data1.MessageStart[1],Data1Array[0],1);
    Move (Data1.MessageStart[2],Data1Array[1],1);
    Move (Data1.MessageStart[3],Data1Array[2],1);
    Move (Data1.MessageStart[4],Data1Array[3],1);
    Move(Data1.MessageID,    Data1Array[4],sizeOf(Word));
    Move(Data1.TransactionNo,Data1Array[6],sizeOf(Integer));
    Move(Data1.TrackerMsgID, Data1Array[10],sizeOf(Byte));
    try
     TCPClient.IOHandler.Write(Data1Array);
     if chkData.checked then MemoClient.Lines.Add('Client sent Data1:'+inttostr(Data1.TransactionNo));
     except
      on E: Exception do
      begin {nothing}
      if chkData.checked then MemoClient.Lines.Add('Exception occured');
      end;
    end;
  end;

Рабочий поток клиента вызывает DataReceived, как только с сервера приходит подтверждающий пакет данных:

procedure TForm1.TimerClientTimer(Sender: TObject);
begin
  SendData1(TransactionNr);
  TransactionNr := TransactionNr + 1;
end;

procedure TForm1.SendData1 (Transaction: Integer);
var Data1      : TData1;
    Data1Array : TIDBytes;
begin

   {fill the data1 record:}
    Data1.MessageStart := '$MSG';       //4 bytes
    Data1.MessageID    := 1;            //2 bytes
    Data1.TransactionNo:= Transaction;  //4 bytes
    Data1.TrackerMsgID := 1;            //1 byte
   {set the length of idBytes to get the dta 1 record:}
    setLength (Data1Array,11);
   {move data1 record to idBytes array:}
    Move (Data1.MessageStart[1],Data1Array[0],1);
    Move (Data1.MessageStart[2],Data1Array[1],1);
    Move (Data1.MessageStart[3],Data1Array[2],1);
    Move (Data1.MessageStart[4],Data1Array[3],1);
    Move(Data1.MessageID,    Data1Array[4],sizeOf(Word));
    Move(Data1.TransactionNo,Data1Array[6],sizeOf(Integer));
    Move(Data1.TrackerMsgID, Data1Array[10],sizeOf(Byte));
    try
     TCPClient.IOHandler.Write(Data1Array);
     if chkData.checked then MemoClient.Lines.Add('Client sent Data1:'+inttostr(Data1.TransactionNo));
     except
      on E: Exception do
      begin {nothing}
      if chkData.checked then MemoClient.Lines.Add('Exception occured');
      end;
    end;
  end;

Рабочий поток клиента вызывает DataReceived, как только пакет данных поступает с сервера:

procedure TForm1.Datareceived (Data : TidBytes);
    var
    MsgStart  : array[1..4] of char;
    s         : string;
    id        : word;
    Data1: TData1;
    Data2: TData2;
    Ack  : TAcknowledgeData1;
    AckArray: TidBytes;
begin
 {get the header:}
  s := bytestostring(Data,0,4);
  if s = '' then exit;
  move (Data[4],id,sizeof(word));
  if (s = '$MSG') then
       begin
      if (ID=3) then
          begin
          move(Data[0],Ack.MessageStart,0);
          Ack.MessageID    := id;
          Move (Data[6],Ack.TransactionNo,SizeOf(Word));
          Move (Data[10],Ack.EventID,SizeOf(Integer));
          LastAck := Ack.TransactionNo;
          end;
       end;
end;

Сервер вызывает процедуру DataReceved, как только клиент отправил пакет Data1. Сервер отправляет пакет подтверждения обратно клиенту:

procedure TForm1.Datareceived (Data : TidBytes);
    var
    MsgStart  : array[1..4] of char;
    s         : string;
    id        : word;
    Data1: TData1;
    Data2: TData2;
    Ack  : TAcknowledgeData1;
    AckArray: TidBytes;
begin
 {get the header:}
  s := bytestostring(Data,0,4);
  if s = '' then exit;
  move (Data[4],id,sizeof(word));
  if (s = '$MSG') then
       begin
      {Server received Data1:}
       if (ID=1) then
          begin
         {get data1:}
          move(Data[0],Data1.MessageStart,0);
          Data1.MessageID    := id;
          move (Data[6],Data1.TransactionNo,sizeOf(Integer));
          move (Data[10],Data1.TrackerMsgID,sizeOf(Byte));
         {send Acknowledge to client}
          Ack.MessageStart := '$MSG';
          Ack.MessageID    := 3;
          Ack.TransactionNo:= Data1.TransactionNo;
          Ack.EventID      := LastEventID;
          LastEventID      := LastEventID + 1;
          SetLength(AckArray,14);
          Move (Ack.MessageStart[1],AckArray[0],1);
          Move (Ack.MessageStart[2],AckArray[1],1);
          Move (Ack.MessageStart[3],AckArray[2],1);
          Move (Ack.MessageStart[4],AckArray[3],1);
          Move (Ack.MessageID,AckArray[4],sizeOf(Word));
          Move (Ack.TransactionNo,AckArray[6],sizeOf(Integer));
          Move (Ack.EventID,AckArray[10],sizeOf(Integer));
            try
            ClientPortIdContext.Connection.IOHandler.Write(AckArray);
            except
            end;
          end; 
...