【问题标题】:How to read all bytes from server using Indy Client in Delphi?如何在 Delphi 中使用 Indy Client 从服务器读取所有字节?
【发布时间】:2012-11-30 02:49:47
【问题描述】:

我正在使用 Indy 客户端来读取服务器发送给我(客户端)的消息。它一口气向我发送了 512 字节的数据。这 512 字节的数据由两种数据类型(Word 和 String)组成。例如,它发送 2 个字节的字,然后再发送 2 个字节的字,然后是 50 个字节的字符串,依此类推。我正在尝试以下代码来解决这个问题:

var BufferArray : Array[0..512] of Byte;

 if IdTCPClient1.IOHandler.InputBufferIsEmpty then
 begin
      if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
      begin
          Edit1.Text := idtcpclient1.IOHandler.ReadBytes(BufferArray ,512, true);
      end;
 end;

我在 Edit1.Text := idtcpclient1.IOHandler.ReadBytes(BufferArray ,512, true);错误:实际和形式 var 参数的类型必须相同。

我使用的方法是否正确。我想在 Edit1.Text 上存储整个 512 个字节,然后对这些数据做任何我想做的事情。请帮助我从服务器获取所有 512 字节。

更新:替代方法

我正在使用这种方法来读取单词和字符串值

WordArray : array[0..5] of word;

 if IdTCPClient1.IOHandler.InputBufferIsEmpty then
 begin
      if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
      begin
        i := 0;
        while i < 6 do //Read all the words
        begin
            //Fill WORD data in array
            WordArray[i] :=  (IdTCPClient1.Socket.ReadWord(True));
        end;
      end;
end;

类似字符串的方法

WordArray[i] := (IdTCPClient1.Socket.ReadString(50));

工作正常,但我必须在循环读取所有数据时保持连接打开。如果中间连接中断,我会丢失所有内容,必须再次从服务器请求整个包。

【问题讨论】:

  • BufferArray 使用动态数组,将您的声明更改为var BufferArray: TBytes;
  • 是的,也一样。有效。谢谢。现在我还有更多问题。我如何遍历这个 BufferArray?我想将它的所有内容存储到一个字符串数组中。
  • @TLama - 更新了我的问题。我的新方法对吗?
  • 对不起,我放弃这个问答;你要我解决你客户的整个沟通部分。那就是我没有时间也没有动力。您发布的文档没有说明单词值的字节顺序,也没有说明字符串的发送或终止方式(我们向您询问的所有内容)。对于您的更新,如果您以前想读取整个 512B 数据包,为什么现在尝试从套接字读取字值?你有没有看过我的回答并理解它的用途(到上次更新时,我已经修改了字值的字节顺序)?
  • @Tlama 但是为什么你用那种乏味的显式方式来反转订单? docwiki.embarcadero.com/Libraries/XE2/en/System.Swap

标签: delphi delphi-xe2 indy indy10


【解决方案1】:

很难回答你,除非你准确地描述了你所拥有的文档中所写的内容。到目前为止,我们知道您的 512B 数据包由 6 个单词和 10x50B 字符串组成。因此,请以此为起点,直到您告诉我们更多信息:

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Buffer: TBytes;
  WordArray: array[0..5] of Word;
  StringArray: array[0..9] of AnsiString;
begin
  if IdTCPClient1.IOHandler.InputBufferIsEmpty then
  begin
    if IdTCPClient1.IOHandler.CheckForDataOnSource(1000) then
      IdTCPClient1.IOHandler.ReadBytes(Buffer, 512, False);

    for I := 0 to High(WordArray) do
    begin
      WordRec(WordArray[I]).Hi := Buffer[I * 2];
      WordRec(WordArray[I]).Lo := Buffer[I * 2 + 1];
    end;
    for I := 0 to High(StringArray) do
      SetString(StringArray[I], PAnsiChar(@Buffer[I * 50 + 12]), 50);

    // here you have the arrays prepared to be processed
  end;
end;

【讨论】:

  • 好的,试试这个。如果成功了,我会非常感谢你
  • 我不希望它会起作用,单词部分可能已经交换了字节,而字符串部分预计您正在接收 ANSI 字符,所以两者都只是猜测。我完全不知道您的服务器如何发送字符串,如果它们是 ANSI,Unicode,它们是如何终止的(这可能是不寻常的,我不知道)。我也不知道这些词是如何发送的,以哪个字节顺序发送。您必须添加这些信息才能让我以此来完善答案。
  • 如果不行,我会和你分享一个word文档,显示数据包的确切格式。
【解决方案2】:

1:字符串的字符集是什么?它是 1 字节的 windows-1251 吗?还是 2 字节的 Unicode UCS-2 ?还是可变长度的 UTF-8 或 UTF-16?

2:字符串的长度是多少?总是 50?


读取缓冲区:

  1. 阅读手册
    1.1 http://www.indyproject.org/docsite/html/TIdIOHandler_ReadBytes@TIdBytes@Integer@boolean.html
    1.2http://www.indyproject.org/docsite/html/TIdIOHandler_ReadSmallInt@Boolean.html 1.3http://docwiki.embarcadero.com/Libraries/XE2/en/System.SetString
  2. 根据类型和参数描述准确地编写代码。
    2.1 阅读标题:这应该会导致类似

    var  Word1, Word2: word;
    
    Word1 := IOHandler.ReadSmallInt(false);   
    Word2 := IOHandler.ReadSmallInt(false);
    
  3. 读取单字节字符串
    3.1 读取缓冲区
    3.2 缓冲区转字符串

    var  Word1, Word2: word;  Buffer: TIdBytes;    
    var s: RawByteString;     
     // or AnsiString; or maybe UTF8String; but probably not UnicodeString aka string
    
    Word1 := IOHandler.ReadSmallInt(false);   
    Word2 := IOHandler.ReadSmallInt(false);
    
    // You should check that you really received 50 bytes,
    // then do something like that:
    
    IOHandler.ReadBytes(Buffer, 50, false);    
    Assert(Length(Buffer)=50);    
    SetString (s, pointer(@Buffer[0]), 50);    
    
  4. 继续阅读其余部分 - 您只阅读了 512 字节数据包的 50+2+2 = 54 个字节 - 应该还有更多数据。

512 = 54*9+26 - 所以它可能看起来像一个循环 - 并丢弃 26 字节尾部。

    var Word1, Word2: word;  Buffer: TIdBytes;    
    var s: RawByteString;     

    for i := 1 to 9 do begin    
      Word1 := IOHandler.ReadSmallInt(false);     
      Word2 := IOHandler.ReadSmallInt(false);  

      IOHandler.ReadBytes(Buffer, 50, false);        
      Assert(Length(Buffer)=50);      
      SetString (s, pointer(@Buffer[0]), 50);    

      SomeOutputCollection.AppendNewElement(Word1, Word2, s);   
    end;   
    IOHandler.ReadBytes(Buffer, 512 - 9*(50+2+2), false); // discard the tail

【讨论】:

  • @TLama ReadString 返回 UnicodeString。主题启动器告诉大约 50 个字节。存在潜在的混淆 - 我在答案的标题中指出。但就主题起始者而言,它保留了原始的“50 字节”声明——对我来说就是这样。直到 Indy 引入ReadAnsiString(const codepage:word; const length: cardinal); 方法
  • 是的,我意识到了这一点并删除了我的评论...抱歉。
  • 让我纠正自己并详细说明我想如何解析我的 512 数据包。我有这个数据包的文档,上面说会有 6 个 2 字节的字,然后会有 50 字节的字符串。现在我必须将单词存储在不同的单词数组中,并将字符串存储在不同的字符串数组中。现在如何从这个数据包中提取这些东西?
  • @TLama - 谢谢你的评论,真的。由于它使您感到困惑-它可能会使任何读者感到困惑。所以让我们继续澄清吧。
  • @Naresh 512 = 6*2 + 10*50,是吗?如果我没有算错?我向您展示了读取 Word 值的代码,并向您展示了读取 50 字节 AnsiString 的代码。只需运行一个代码 6 次,另一个代码运行 10 次。但是,如果单词是 big-endian 或 little-endian,您应该查阅文档 - 也许您需要 ReadSmallInt(true) 代替。您还需要知道这些 50 字节字符串的字符集。最后 - 我简直不敢相信那些正好有 50 个字节的字符串,也许它们是 C 字符串?然后完成后,您必须在字符串中找到终止符并调整其实际长度。
猜你喜欢
  • 2010-10-07
  • 2017-02-15
  • 2012-10-29
  • 1970-01-01
  • 2015-01-27
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
相关资源
最近更新 更多