【问题标题】:How to supress echo with TIdTelnet?如何使用 TO Telnet 抑制回声?
【发布时间】:2013-06-17 09:43:49
【问题描述】:

使用 Indy10 - DelphiXE。 如何告诉我正在使用TIdTelnet 连接的 telnet 服务器不回显我发送给 telnet 服务器的命令?

这是我当前的尝试(不起作用),我尝试在每次 telnet 写入时发送序列 IAC DO SUPPRESS_LOCAL_ECHO,但我相信这必须在协商阶段完成?

uses
  TIdTelnet,

...
procedure TIOTelnetConnection.SendSupressEcho;

var
  Resp: TIdBytes;

begin
 SetLength(Resp, 3);
 Resp[0] := TNC_IAC;
 Resp[1] := TNC_DO;
 Resp[2] := TNO_SUPLOCALECHO;
 FIOConnection.IOHandler.Write(Resp);
end;

procedure TIOTelnetConnection.Write(Str: AnsiString);
begin
 SendSupressEcho;
 if Str <> '' then
  FIOConnection.IOHandler.Write(Str);
end;

查看TIdTelnet 源,我看到Negotiate 过程,但它受到保护,我该如何覆盖它的行为?

【问题讨论】:

    标签: delphi telnet indy10


    【解决方案1】:

    如果服务器向您发送IAC WILL ECHO,则TIdTelnet 被硬编码为发送IAC DO ECHO 响应,然后触发OnTelnetCommand(tncNoLocalEcho) 事件告诉您不要在本地回显您发送的内容。

    如果服务器向您发送IAC WONT ECHO,则TIdTelnet 被硬编码为发送IAC DONT ECHO 响应,然后触发OnTelnetCommand(tncLocalEcho) 事件以告诉您在本地回显您发送的内容。

    如果服务器向您发送IAC DO ECHO,则TIdTelnet 被硬编码为发送IAC WILL ECHO 响应,然后触发OnTelnetCommand(tncEcho) 事件告诉您回显收到的任何内容。

    如果服务器向您发送IAC DONT ECHO,则TIdTelnet 被硬编码为发送IAC WONT ECHO 响应,然后触发OnTelnetCommand(tncLocalEcho) 事件告诉您回显收到的任何内容。

    因此,如果您不希望服务器回显给您,您可以向服务器发送IAC DONT ECHO 命令,而不是IAC DO SUPLOCALECHO 命令。然后服务器将相应地回复IAC WONT ECHOIAC WILL ECHO(显然TIdTelnet将回复,但服务器不会再次回复,因为它的ECHO当前状态没有改变,避免了无休止的响应循环)。

    【讨论】:

    • 谢谢,我发现它有点混乱,但 IAC DONT ECHO 确实是我正在寻找的序列。你有没有机会在 Indy 的后续版本中将其更改为属性?
    • TIdTelnet 非常简单,所以我相信在未来的版本中可以做很多事情来让它变得更好。
    【解决方案2】:

    @Remy Lebeau:据我了解,您是在说: 如果服务器发送一个 IAC DO ECHO 给你,TIdTelnet 被硬编码发送一个 IAC WILL ECHO 响应,然后触发一个 OnTelnetCommand( tncEcho ) 事件告诉你回显您收到的任何内容。

    这是有道理的,并且与 RFC857 所要实现的目标一致......

    但是,在我们的代码中:

    procedure TIdTelnet.Negotiate;
    ...
          TNC_DONT:
            begin
              b := IOHandler.ReadByte;
              case b of
                TNO_ECHO:
                  begin
                    DoTelnetCommand(tncEcho);
                    //DoStatus('ECHO');    {Do not Localize}
                    Reply := TNC_WONT;
                  end;
                else
                  Reply := TNC_WONT;
              end;
    

      TNC_DO:
        begin
          b := IOHandler.ReadByte;
          case b of
            TNO_ECHO:
              begin
                Reply := TNC_WILL;
                DoTelnetCommand(tncLocalEcho);
              end;
    

    这个代码肯定不正确吗? (在 Indy 的 10.6.0.497 版本中)

    我相信这会更有意义:

     procedure TIdTelnet.Negotiate;
     ...
          TNC_DONT:
             begin
               b := IOHandler.ReadByte;
               case b of
                 TNO_ECHO:
                   begin
                   // Agree not to echo back everything received from server 
                   // (This being the default - you shouldn't echo unless asked to)
                      Reply := TNC_WONT;            
                   // Therefore only print locally what is sent to server
                   // (Again: this is the default behavior without negotiation) 
                      DoTelnetCommand(tncLocalEcho);
                   end;
                 else
                   Reply := TNC_WONT;
               end;
    

       TNC_DO:
         begin
           b := IOHandler.ReadByte;
           case b of
             TNO_ECHO:
               begin
               // Agree to echo back everything received from server 
                  Reply := TNC_WILL;
                  DoTelnetCommand(tncEcho); 
               // Therefore you may still have to locally print what you send
               // (i.e. local echo is usually still implicit in this)
               end;   
    

    换句话说,我相信代码现在已经从它应该是交换的 - 即发送 DO ECHO 的服务器应该得到 tncEcho 令牌 - 这就是你在上面的引用中所说的!

    这个错误是如何存活这么久的? (可能是因为大多数 Telnet 服务器不再需要 RFC857 回显协商)

    不幸的是,目前我认为“补偿”此错误的唯一方法是创建 IDTelnet.pas 文件的副本;在项目管理器中将其链接到您的项目;然后按照上面的说明对该副本进行更正。

    【讨论】:

      【解决方案3】:

      要解决最初的问题:“如何使用 TIdTelnet 抑制回声?”,我建议采取以下步骤:

      1. 在您的表单中添加一个 TIdConnectionIntercept 并将其挂接到您的 TIdTelnet 组件通过使用 Object 的 Intercept 属性 督察。

      2. 为 TIdConnectionIntercept OnSend 生成事件处理程序 事件。

      3. 使用它来检测它的 var ABuffer: TIdBytes 参数何时获取 响应服务器关于回显的协商命令。 请注意,缓冲区将完全且仅具有 由于响应填充和发送的方式,其中的响应 TIdTelnet.协商

      4. 根据需要更改中间字节(即强制将 WILL 更改为 WONT 或 DO 更改为 DONT 以适应您要强制执行的内容)。

      乔布斯是个“好人”

      当然请记住,此技术会在调用 TidTelnet OnTelnetCommand 之后注入更改,因此您必须更改您在该处理程序中设置的任何内容以适应您修改后的响应。

      【讨论】:

      • 不幸的是,我发现在客户端中强制使用 DONT 会导致用 Delphi XE4 编写的 SERVER 在 IOHandler 中引发断言异常!这似乎是因为 TidTelnet 中的硬编码产生了额外的响应:即我们得到了 WILL -> DONT -> WONT -> DONT 的序列(最后一个 DONT 不应该发生)。意外的 3 字节命令可能会导致服务器(在启用 Unicode 支持的情况下编写时)检测到“丢失字节”并引发断言异常。
      • 总结一下:(a) TidTelnet 将在 WILL-DONT-WONT 序列中过于频繁地回答一次,并且 (b) TidTelnetServer 可能存在与接收意外奇数字节序列有关的错误。跨度>
      • 这可能是因为我不得不使用 result := AContext.Connection.IoHandler.WaitFor(chr(TNC_IAC),False,False,Indy8BitEncoding,Timeout);在服务器代码中,因为“WaitFor”没有 TidByte 重载。忽略过载可能是问题的核心——尽管我仍然认为意外传入数据导致服务器崩溃并导致服务器断开连接并不是很好!
      • 注入 WONT 时也会发生同样的事情:即我们得到一个 DO-WONT-DONT-WONT 序列,其中最后一个 WONT 在服务器中引发断言异常。关闭断言会停止崩溃,但服务器随后会收到两个意外字符(3 字节 WONT 的最后 2 个字节)与其正常文本输入混合。所以在我看来,unicode 支持和 Telnet 还没有得到妥善解决。
      • 目前,我通过检测 TidConnectionIntercept OnSend 处理程序中 3 字节 WONT 和 DONT 响应的不必要重新传输并将每个设置为三个空值(0,0,0 )。这会阻止服务器引发断言错误,因为它不会尝试解释意外命令。发送三个空值是一件非常安全(尽管很烦人)的事情。不幸的是,您似乎不能使用 OnSend 来删除缓冲区。
      猜你喜欢
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-07
      • 2011-02-08
      • 2013-01-07
      • 2020-01-23
      • 1970-01-01
      相关资源
      最近更新 更多