【问题标题】:Delphi Indy IdTCPServer and IdTCPClient. read and write control characters and textDelphi Indy IdTCPServer 和 IdTCPClient。读写控制字符和文本
【发布时间】:2016-04-23 21:04:08
【问题描述】:

德尔福 XE3,印地 10.5.9.0

我正在创建计算机和仪器之间的接口。仪器采用 ASTM 协议。

我已经成功地在服务器和客户端之间来回发送基于文本的消息。我已经能够将控制字符发送到服务器并读取它们。经过 3 天的搜索,我还没有弄清楚如何编写和阅读混合了控制字符和文本的消息。

我正在发送需要控制字符和文本的 ASTM 协议消息,如下行所示。尖括号中的所有内容都是控制字符。写信息不是我遇到问题的地方。这是在阅读它的时候,因为我会收到文本和控制字符。下面的代码是我读取控制字符的方式。如何判断何时获取字符是否为控制字符以及何时为同一控制字符串和文本字符中的文本?感谢 Remy Lebeau 和他在这个网站上的帖子让我到达了现在的位置。他谈到了如何使用缓冲区,但我不知道如何读取包含控制字符和文本字符的缓冲区。

<STX>3O|1|G-13-00017||^^^HPV|R||||||N||||||||||||||O<CR><ETX>D3<CR><LF>

我已将以下代码添加到我的服务器组件 OnConnect 事件中,该事件应该允许我发送控制字符...

...
AContext.Connection.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
...

我的服务器 OnExecute 事件...

procedure TTasksForm.IdTCPServer1Execute(AContext: TIdContext);
var
  lastline : WideString;
  lastcmd :  WideString ;
  lastbyte : Byte ;
begin 

  ServerTrafficMemo.Lines.Add('OnExecute') ;

  lastline := '' ;
  lastcmd := '' ;

  lastbyte := (AContext.Connection.IOHandler.ReadByte) ;

  if lastbyte = Byte(5) then
  begin

    lastcmd := '<ENQ>' ;

    ServerTrafficMemo.Lines.Add(lastcmd) ;

    AContext.Connection.IOHandler.WriteLn(lastcmd + ' received') ;

  end;

end;

【问题讨论】:

    标签: delphi indy


    【解决方案1】:

    唯一存在的控制字符是STXETX,它们都是

    对于这种类型的数据,使用 Indy 有几种不同的读取方式。由于大部分数据是文本数据,并且控制字符仅用作帧分隔符,因此最简单的方法是使用带有显式终止符的 IOHandler.ReadLn()IOHandler.WaitFor()

    当然,还有其他的选择,比如直接从IOHandler.InputBuffer读取字节(我觉得这种情况下有点过分了),使用InputBuffer.IndexOf()的方法知道要读取多少字节。

    另外,TIdTCPServer 是一个多线程组件,它的事件在工作线程中触发,但您的代码直接访问 UI,这不是线程安全的。您必须与 UI 线程同步。

    你也不应该是WideString。请改用(Unicode)String

    试试这样的:

    procedure TTasksForm.IdTCPServer1Connect (AContext: TIdContext);
    begin AContext.Connection.IOHandler.DefStringEncoding := Indy8BitEncoding;
    end;
    
    procedure TTasksForm.IdTCPServer1Execute(AContext: TIdContext);
    var
      lastline : string;
      lastcmd :  string ;
      lastbyte : Byte ;
    begin
      TThread.Synchronize(nil,
        procedure
        begin
          ServerTrafficMemo.Lines.Add('OnExecute') ;
        end
      );
    
      lastbyte := (AContext.Connection.IOHandler.ReadByte);
    
      if lastbyte = $5 then
      begin
        lastcmd := '<ENQ>' ;
        TThread.Synchronize(nil,
          procedure
          begin
            ServerTrafficMemo.Lines.Add(lastcmd) ;
          end
        );
      end
      else if lastbyte = $2 then
      begin
        lastline := #2 + AContext.Connection.IOHandler.ReadLn(#3) + #3;
        lastline := lastline + AContext.Connection.IOHandler.ReadLn(#13#10) + #13#10;
        { or:
        lastline := #2 + AContext.Connection.IOHandler.WaitFor(#3, true, true);
        lastline := lastline + AContext.Connection.IOHandler.WaitFor(#13#10, true, true);
        }
        lastcmd := '<STX>' ;
        TThread.Synchronize(nil,
          procedure
          begin
            ServerTrafficMemo.Lines.Add(lastcmd) ;
          end
        );
      end;
      AContext.Connection.IOHandler.WriteLn(lastcmd + ' received') ;
    end;
    

    【讨论】:

    • 哇!我正在努力消化这篇文章中的所有好东西!为什么在 'lastbyte =' 行中使用 $ 而在 ReadLn 行中使用 #?
    • lastbyte 是一个字节。 $5 是一个数值,相当于Byte(5),只是更简洁。 ReadLn()WaitFor() 对字符串而不是字节进行操作。 #2#3 是字符值(Char(2)Char(3))。
    • @DonaldAdams 是的。 IOHandler 有一个ReadTimeout 属性,它的ReadLn()WaitFor() 方法也有一个可选的ATimeout 输入参数。如果未指定ATimeout,则使用ReadTimeout。大多数读取操作会在超时时引发EIdReadTimeout 异常,ReadLn() 除外。它将改为以空白字符串退出并将IOHandler.ReadLnTimedOut 属性设置为true。
    【解决方案2】:

    我不知道如何读取包含控制字符和文本字符的缓冲区

    这个协议无疑是使用 ASCII 字符串。任何低于十进制 32 的字符都将是控制字符。那些 32 及以上将是数据字符。看 http://ascii-table.com/ascii.php

    将其作为字节处理可以正常工作。您还可以使用 ansistring,它是 ASCII 加上前 127 个字符。在这种情况下,我会避免使用 UTF(any) 并坚持使用字节或 ansistring。您需要在字符级别控制消息,这些字符是每个字符 8 位,没有转义。

    还有see the first example, in the first answer here:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-05
      • 1970-01-01
      • 2016-02-24
      • 2011-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      相关资源
      最近更新 更多