Delphi Indy IdTCPServer和IdTCPClient。读写控制字符和文本

时间:2016-04-23 21:04:08

标签: delphi indy

Delphi XE3,Indy 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;

2 个答案:

答案 0 :(得分:1)

唯一存在的控制字符是STXETX,它们都是&lt; 32,所以ASCII和UTF-8都可以很好地处理它们。或者,您可以使用Indy自己的内置8位编码。

对于这种类型的数据,有几种方法可以用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;

答案 1 :(得分:0)

  

我无法告诉如何读取包含控制字符和文本字符的缓冲区

该协议无疑使用ASCII字符串。小数32以下的任何字符都是控制字符。那些32及以上将是数据字符。看到 http://ascii-table.com/ascii.php

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

此外see the first example, in the first answer here