【问题标题】:Lost data with Indy10 and iDTCPServer and idTCPClient使用 Indy10 和 iDTCPServer 和 idTCPClient 丢失数据
【发布时间】:2020-03-05 03:54:54
【问题描述】:

我有两个程序使用 Delphy 10.3 和 Indy 10 idTCPServer 和 idTCPClient0:

服务器端:

  • 服务器使用定时器定时发送TIDBytes的数据集1(150字节数据)
  • 服务器发送TIDBytes的数据集2但只是不定时发送
  • 服务器使用TIDServer.Connection.IOHandler.Write(IDBytesArray);发​​送数据

客户端:

  • 客户端通过许多示例中描述的线程接收 TIDBytes

问题

  • 如果服务器的计时器确实以低频率(1000 毫秒)发送数据,那么一切正常,客户端会接收两个数据集(一个来自服务器的计时器,另一个来自频繁发送)。
  • 但是,如果服务器的定时器以高频率(每 50 毫秒)发送数据集 1,则无法接收到来自第二个数据集的所有数据(但正在接收定时器发送的所有数据)。

问题 - 这可能是缓冲问题吗?或者计时器是否覆盖了已经在缓冲区中但客户端尚未读取的数据集 2?我有什么办法可以克服这种情况?

【问题讨论】:

  • 请提供minimal reproducible example,在两端显示您的实际代码。 TCP 是一个字节流,IOHandler.Write() 不会以任何方式分隔数据,也不会防止在多个线程中发送时出现并发问题。你有责任在你自己的代码中处理所有这些,但听起来你不是。没有看到你的实际代码很难说。

标签: delphi buffer tcpclient indy tcpserver


【解决方案1】:

Lebeau 确实感谢您的回复。我认为您正确地提到了计时器可以将数据发送到端口,并且它可能会弄乱另一个计时器,该计时器也同时将数据写入端口。 所以我减少了例子:

  • 客户端定期将 Data1 结构的记录作为 tidBytes 发送到服务器。
  • 服务器以确认响应。
  • 客户端检查确认是否属于最后发送的数据集。 只要客户端发送包的速度不超过每 100 毫秒,这种方法就可以正常工作。如果发送说 aevry 10 毫秒,那么客户端会丢失一些确认。 如果有帮助,我可以分享整个演示项目。但这可能不是stackoverflow的目的。所以我在下面添加了一些代码:

定义了两个数据记录: - 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;

客户端有一个定时器,它定期向调用过程 SendData1 的服务器发送 Dataset1:

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;

客户端一发送 Data1 包,服务器就调用 DataReceved 过程。服务器向客户端发送一个确认包:

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; 

【讨论】:

  • 这不是答案。您应该将其发布为对原始问题的修改。
  • 好的,我明白了。我是stackoverflow写作的新手。对不起,我以后会做得更好的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多