【问题标题】:Delphi XE3, Indy TCPServer and TCPClient. transmit and receive CRLF control codesDelphi XE3、Indy TCPServer 和 TCPClient。发送和接收 CRLF 控制码
【发布时间】:2016-04-30 22:56:18
【问题描述】:

我在之前的帖子中得到了帮助,但出现了另一个问题,我不知道如何处理评论。

我需要知道如何在行尾使用 CRLF 执行 WriteLn。我以为我要发送一个,但我认为我处理错了,因为它似乎没有结束。这是其中一条消息的框架。这是消息的人类可读版本,通过将人类可读版本替换为实际控制代码来进行转换。

<STX>1H|\^~||||||||||P|1|20150724124402<CR><ETX>91<CR><LF>
<STX>2P|1|PP00015906|||Sox^White||19550506||||||<CR><ETX>56<CR><LF>
<STX>3O|1|G-13-00005||^^^CT/GC|R||||||N||||||||||||||O<CR><ETX>23<CR><LF>
<STX>4O|2|G-13-00005||^^^HPV^HPV|R||||||N||||||||||||||O<CR><ETX>F6<CR><LF>
<STX>5L|1|N<CR><ETX>DF<CR><LF>

我认为当我将消息从人类可读的“代码”转换为实际的控制代码时,我可能做错了什么。框架中间的控制代码似乎正常。帧的末尾可能不会结束。

          // replace human readable codes with control codes
          pantherastmframe := StringReplace(pantherastmframe, '<STX>', #2, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<CR>', #13, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<ETX>', #3, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<LF>', #10, [rfReplaceAll] ) ;

我在客户端使用以下代码来检查控制代码。

      MsgIn := PantherIdTCPClient.IOHandler.ReadLn ;

      crlffound := 'False' ;

      if Pos( #13#10, MsgIn) > 0 then
        crlffound := 'True' ;

为了清楚起见,我已经包含了完整的代码。

OnConnect 代码。

procedure TTasksForm.IdTCPServer1Connect(AContext: TIdContext);
begin

  AContext.Connection.IOHandler.DefStringEncoding := Indy8BitEncoding ;

  ServerTrafficMemo.Lines.Add(FormatDateTime( 'yyyy-mm-dd hh:nn:ss.zzz',     now ) +'  OnConnect') ;

  // connected message
  AContext.Connection.IOHandler.WriteLn('Connected');

end;

OnDisconnect 代码。

procedure TTasksForm.IdTCPServer1Disconnect(AContext: TIdContext);
begin

  ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
                                             '  OnDisconnect') ;
end;

这是 OnExecute 代码。

procedure TTasksForm.IdTCPServer1Execute(AContext: TIdContext);
var
  lastline : String;
  lastcmd :  String ;
  lastbyte : Byte ;
  i : integer ;
  pantherastmframe : string ;
begin

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

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

  if lastbyte = $5 then  //  <ENQ>
  begin

    lastcmd := '<ENQ>' ;

    TThread.Synchronize(nil,
      procedure
      begin
        ServerTrafficMemo.Lines.Add( FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
          '  <=  <ENQ>') ;
      end );

    AContext.Connection.IOHandler.Write(Byte(6)) ; // <ACK>

    TThread.Synchronize(nil,
      procedure
      begin
        ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
          '  =>  <ACK>') ;
      end );

    LastPantherByte := lastbyte  ; // <ENQ>

  end

  else

  if lastbyte = $4 then  // <EOT>
  begin

    TThread.Synchronize(nil,
      procedure
      begin
        ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd  hh:nn:ss.zzz', now) +
          '  <=  <EOT>') ;
      end );

    // are there messages to transmit
    if ConsolidatedASTMMessagesMemo.Lines.Count > 0 then
    begin

      // turn off panther timer
      TThread.Synchronize(nil,
        procedure
        begin
          PantherProcessTimer.Enabled := False ;
        end );

      TThread.Synchronize(nil,
        procedure
        begin
          ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
            '  =>  <ENQ>') ;
        end );

      AContext.Connection.IOHandler.Write(Byte(5)) ; // <ENQ>

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

      if lastbyte = $6 then  // <ACK>
      begin

        lastcmd := '<ACK>' ;

        TThread.Synchronize(nil,
          procedure
          begin
            ServerTrafficMemo.Lines.Add( FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
              '  <=  <ACK>') ;
          end );

        // transmit frames
        for i := 0 to ConsolidatedASTMMessagesMemo.Lines.Count-1 do
        begin


          pantherastmframe := ConsolidatedASTMMessagesMemo.Lines[i] ;

          TThread.Synchronize(nil,
            procedure
            begin
              ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
                '  =>  ' + pantherastmframe) ;
            end );

          // replace human readable codes with control codes
          pantherastmframe := StringReplace(pantherastmframe, '<STX>', #2, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<CR>', #13, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<ETX>', #3, [rfReplaceAll] ) ;
          pantherastmframe := StringReplace(pantherastmframe, '<LF>', #10, [rfReplaceAll] ) ;

           AContext.Connection.IOHandler.WriteLn(pantherastmframe) ;

           // wait for <ACK>

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

           if lastbyte = $6 then  // <ACK>
           begin

             TThread.Synchronize(nil,
               procedure
                 begin
                   ServerTrafficMemo.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
                    '  <=  <ACK>') ;
                end );

            end

            else

            if lastbyte = $21 then  // <NAK>
            begin

              TThread.Synchronize(nil,
                procedure
                begin
                  ServerTrafficMemo.Lines.Add( FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
                    '  <=  <NAK>') ;
                end );

            end ;

        end ;   // transmit frames

        TThread.Synchronize(nil,
          procedure
          begin
            ServerTrafficMemo.Lines.Add( FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', now) +
              '  <=  <EOT>') ;
          end );

        // tell panther you are done for now
        AContext.Connection.IOHandler.WriteLn(#4) ; // <EOT>

        LastPantherByte := lastbyte ; // <EOT>

        ConsolidatedASTMMessagesMemo.Clear ;

      end ;

      // turn on panther timer
      TThread.Synchronize(nil,
        procedure
        begin
          PantherProcessTimer.Enabled := True ;
        end );

    end ;

  end;

end;

【问题讨论】:

    标签: delphi


    【解决方案1】:

    WriteLn() 确实在指定数据的末尾发送了CRLF。真正的问题是您期望ReadLn() 在其输出中包含CRLF,但实际上并没有。 ReadLn() 等待指定的终止符(默认为LF),然后从输出中剥离终止符(如果使用的终止符是LF 并且接收到CRLF,则整个CRLF 被剥离)。您的代码没有考虑到这一点,例如:

    MsgIn := PantherIdTCPClient.IOHandler.ReadLn + #13#10;
    // or, if you do not actually need the CRLF in the output:
    // MsgIn := PantherIdTCPClient.IOHandler.ReadLn;
    
    // no need for checking Pos()...
    crlffound := 'True';
    

    请注意,如果您将 IOHandler 的ReadTimeout 属性或ReadLnATimeout 参数设置为非无限超时,那么ReadLn() 将在超时时返回一个空白字符串并设置ReadLnTimedOut属性为 True,而不是引发异常:

    MsgIn := PantherIdTCPClient.IOHandler.ReadLn + #13#10;
    if PantherIdTCPClient.IOHandler.ReadLnTimedOut then
    begin
      // do something...
    end else
    begin
      crlffound := 'True';
      // do something else...
    end;
    

    另请注意,默认情况下,ReadLn() 将裸 LF 和 CRLF 同等处理为行终止符。如果您只想专门处理 CRLF,可以在可选的ATerminator 参数中指定 CRLF:

    ReadLn(#13#10)
    

    综上所述,另一种选择是使用WaitFor() 而不是ReadLn()WaitFor() 有一个可选的 AInclusive 参数,您可以将其设置为 True 以在输出中包含 CRLF。如果读取超时,它也会引发异常:

    MsgIn := PantherIdTCPClient.IOHandler.WaitFor(#13#10, True, True);
    // no need for Pos() here...
    crlffound := 'True';
    

    OnConnect 代码。
    OnDisconnect 代码。

    您的事件处理程序中有线程不安全的代码。事件处理程序在TIdTCPServer 内部的工作线程中运行,但它们直接访问 UI,这是不安全的。您必须在访问ServerTrafficMemo 时与主 UI 线程同步,就像您在 OnExecute 事件处理程序中所做的一样(尽管您没有同步对 ConsolidatedASTMMessagesMemo 的访问,但您必须 这样做)。

    【讨论】:

      【解决方案2】:

      如果您可以控制该代码的两端,并且我认为是数据传输,那么您应该为自己学习 RESTful 的东西。它会让你的代码更加灵活,它会给你一些强大的能力来编写更清晰的抽象。这有助于加快开发速度。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-03
        • 1970-01-01
        • 1970-01-01
        • 2014-12-30
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多