Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 如何可靠地从indy TCP';什么是输入缓冲区?_Multithreading_Delphi_Tcp_Network Programming_Indy - Fatal编程技术网

Multithreading 如何可靠地从indy TCP';什么是输入缓冲区?

Multithreading 如何可靠地从indy TCP';什么是输入缓冲区?,multithreading,delphi,tcp,network-programming,indy,Multithreading,Delphi,Tcp,Network Programming,Indy,我有一段代码,当许多数据包几乎同时从同一个源到达时,大部分时间都会跳过数据包。数据包被构建成这样,一个数据包大小字段被附加在开始处,该字段后面有数据包的大小,以字节为单位 TCP客户端线程以10毫秒的间隔运行 线程库 TCP线程代码 如何确保零数据包丢失?您的TCP读取代码存在两个主要问题: 在第二次调用ExtractToBytes()之前,您没有确保InputBuffer实际具有MsgSize可用字节数。如果试图提取的字节数超过缓冲区中的实际字节数,ExtractToBytes()将引发异常

我有一段代码,当许多数据包几乎同时从同一个源到达时,大部分时间都会跳过数据包。数据包被构建成这样,一个数据包大小字段被附加在开始处,该字段后面有数据包的大小,以字节为单位

TCP客户端线程以10毫秒的间隔运行

线程库 TCP线程代码
如何确保零数据包丢失?

您的TCP读取代码存在两个主要问题:

  • 在第二次调用
    ExtractToBytes()
    之前,您没有确保
    InputBuffer
    实际具有
    MsgSize
    可用字节数。如果试图提取的字节数超过缓冲区中的实际字节数,
    ExtractToBytes()
    将引发异常

  • 更重要的是,在每次调用
    ExtractToBytes()
    之前,您没有将
    缓冲区的大小调整回0。在第一次循环迭代中的第一次调用之后,
    缓冲区的长度为4字节。如果该消息的大小小于4个字节,则在
    缓冲区的末尾留下随机字节,这些字节将被传递到解析器,并可能破坏其逻辑。但更糟糕的是,如果缓冲区中存在另一个消息大小,则下一次循环迭代将第三次调用
    ExtractToBytes()
    ,并将这4个字节追加到现有
    缓冲区的末尾
    内容,not将按照您的假设替换该内容(默认情况下,
    ExtractToBytes()
    aaappend
    参数为True)。因此,您最终会将上一条消息数据中的4个字节复制到
    MsgSize
    变量中,而不是刚刚提取的新4个字节,因此在下一条
    ExtractToBytes()上使用损坏的
    MsgSize
    打电话

  • 由于您的数据包以长度为前缀,因此您根本不需要使用
    CheckForDataOnSource()
    或直接访问
    InputBuffer
    。使用以下代码,让Indy为您完成工作:

    procedure TForm1.THEX_TCP;
    var
      Buffer  : TBytes;
      MsgSize : Integer;
    begin
      MsgSize := TCPClient.IOHandler.ReadLongInt;
      TCPClient.IOHandler.ReadBytes(Buffer, MsgSize);
      Inc(fRXCount);
      NAT.RecievedNATData(Buffer);
    end;
    
    默认情况下,这将阻止调用者,直到有数据可读取。如果没有数据可读取时需要退出X_TCP
    ,请改用如下方式:

    procedure TForm1.THEX_TCP;
    var
      Buffer  : TBytes;
      MsgSize : Integer;
    begin
      if TCPClient.IOHandler.InputBufferIsEmpty then
      begin
        TCPClient.IOHandler.CheckForDataOnSource;
        TCPClient.IOHandler.CheckForDisconnect;
        if TCPClient.IOHandler.InputBufferIsEmpty then Exit;
      end;
    
      repeat
        MsgSize := TCPClient.IOHandler.ReadLongInt;
        TCPClient.IOHandler.ReadBytes(Buffer, MsgSize);
        Inc(fRXCount);
        NAT.RecievedNATData(Buffer);
        SetLength(Buffer, 0);
      until TCPClient.IOHandler.InputBufferIsEmpty;
    end;
    
    这种方法的唯一缺点是
    ReadLongInt()
    ReadBytes()
    可能会将更多字节读入
    InputBuffer
    ,因此,如果在短时间内发送大量数据,则循环可能会运行很长时间。如果您一次只能读取一个缓冲区,并且只能处理完整的消息,请使用以下方法:

    procedure TForm1.THEX_TCP;
    var
      MsgSizeBuffer: array[0..3] of Byte;
      MsgSize, I : Integer;
      Buffer : TBytes;
    begin
      TCPClient.IOHandler.CheckForDataOnSource;
      TCPClient.IOHandler.CheckForDisconnect;
    
      while TCPClient.IOHandler.InputBuffer.Size >= 4 do
      begin
        // unfortunately, TIdBuffer does not have a way to peek
        // multiple bytes at a time without removing them
        for I := 0 to 3 do
          MsgSizeBuffer[I] := TCPClient.IOHandler.InputBuffer.Peek(I);
        Move(MsgSizeBuffer[0], MsgSize, 4);
        MsgSize := LongInt(GStack.NetworkToHost(LongWord(MsgSize)));
    
        if TCPClient.IOHandler.InputBuffer.Size < (4+MsgSize) then
          Break;
    
        TCPClient.IOHandler.InputBuffer.Remove(4);
        TCPClient.IOHandler.InputBuffer.ExtractToBytes(Buffer, MsgSize);
        Inc(fRXCount);
        NAT.RecievedNATData(Buffer);
        SetLength(Buffer, 0);
      end;
    end;
    
    程序TForm1.THEX\U TCP; 变量 MsgSizeBuffer:字节数组[0..3]; MsgSize,I:整数; 缓冲区:t字节; 开始 TCPClient.IOHandler.checkforDataSource; TCPClient.IOHandler.CheckForDisconnect; 而TCPClient.IOHandler.InputBuffer.Size>=4 do 开始 //不幸的是,TIdBuffer没有一种窥视的方式 //一次多个字节而不删除它们 对于I:=0到3 do MsgSizeBuffer[I]:=TCPClient.IOHandler.InputBuffer.Peek(I); 移动(MsgSizeBuffer[0],MsgSize,4); MsgSize:=LongInt(GStack.NetworkToHost(LongWord(MsgSize)); 如果TCPClient.IOHandler.InputBuffer.Size<(4+MsgSize),则 打破 TCPClient.IOHandler.InputBuffer.Remove(4); TCPClient.IOHandler.InputBuffer.ExtractToBytes(缓冲区,MsgSize); 公司(fRXCount); NAT.接收数据(缓冲器); SetLength(缓冲区,0); 结束; 结束;
    您的代码有错误;TCPClient.IOHandler.ReadLongInt()的参数应为False。否则,它将启用NetworkToHost转换。大多数协议使用网络字节顺序发送整数。在设计自定义协议时,您应遵循相同的规则。因此
    写入(Longint)
    ReadLongInt()
    默认情况下执行转换。您当然可以在需要时手动设置参数,这就是它存在的原因。
    procedure TForm1.THEX_TCP;
    var
      Buffer  : TBytes;
      MsgSize : Integer;
    begin
      if TCPClient.IOHandler.InputBufferIsEmpty then
      begin
        TCPClient.IOHandler.CheckForDataOnSource;
        TCPClient.IOHandler.CheckForDisconnect;
        if TCPClient.IOHandler.InputBufferIsEmpty then Exit;
      end;
    
      repeat
        MsgSize := TCPClient.IOHandler.ReadLongInt;
        TCPClient.IOHandler.ReadBytes(Buffer, MsgSize);
        Inc(fRXCount);
        NAT.RecievedNATData(Buffer);
        SetLength(Buffer, 0);
      until TCPClient.IOHandler.InputBufferIsEmpty;
    end;
    
    procedure TForm1.THEX_TCP;
    var
      MsgSizeBuffer: array[0..3] of Byte;
      MsgSize, I : Integer;
      Buffer : TBytes;
    begin
      TCPClient.IOHandler.CheckForDataOnSource;
      TCPClient.IOHandler.CheckForDisconnect;
    
      while TCPClient.IOHandler.InputBuffer.Size >= 4 do
      begin
        // unfortunately, TIdBuffer does not have a way to peek
        // multiple bytes at a time without removing them
        for I := 0 to 3 do
          MsgSizeBuffer[I] := TCPClient.IOHandler.InputBuffer.Peek(I);
        Move(MsgSizeBuffer[0], MsgSize, 4);
        MsgSize := LongInt(GStack.NetworkToHost(LongWord(MsgSize)));
    
        if TCPClient.IOHandler.InputBuffer.Size < (4+MsgSize) then
          Break;
    
        TCPClient.IOHandler.InputBuffer.Remove(4);
        TCPClient.IOHandler.InputBuffer.ExtractToBytes(Buffer, MsgSize);
        Inc(fRXCount);
        NAT.RecievedNATData(Buffer);
        SetLength(Buffer, 0);
      end;
    end;