Java telnet登录:发送用户名后卡住

时间:2015-09-29 21:08:54

标签: java telnet apache-commons-net

我试图用Java执行程序化的telnet会话。我使用commons-net TelnetClient,但我也尝试过直接套接字。无论哪种情况,我都有同样的问题。

我读到"登录:",然后发送用户名,后跟CRLF。什么都没有,没有其他数据被读取或由服务器写入。

telnet服务器位于嵌入式设备(Star打印机)上,所以我想知道是否需要一些特殊的选项,我没有设置,或者不支持commons-net TelnetClient上课。

我可以毫无问题地使用Linux telnet,我可以在OSX中针对telnet服务器运行我的代码,它运行正常。

  TelnetClient client = new TelnetClient();
  client.registerNotifHandler(new TelnetNotificationHandler() {
    @Override
    public void receivedNegotiation(int negotiation_code, int option_code) {
      ALog.i(this, "negotiation code: %d, option code: %d", negotiation_code, option_code);
    }
  });
  try {
    client.addOptionHandler(new TerminalTypeOptionHandler("VT100", false, false, true, false));
    client.addOptionHandler(new SuppressGAOptionHandler(true, false, true, false));
    client.addOptionHandler(new EchoOptionHandler(true, true, true, true));
  } catch (InvalidTelnetOptionException e) {
    e.printStackTrace();
  }

  try {
    FileOutputStream fos = new FileOutputStream("/sdcard/spy.out");
    client.registerSpyStream(fos);
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  }

  InputStream in = null;
  PrintWriter out = null;

  String ip = getIpAddress(p);
  ALog.i(this, "connecting to: %s", ip);
  try {
    client.connect(ip);
    in = client.getInputStream();
    out = new PrintWriter(client.getOutputStream(), true);

    if (!expect(in, "login: ", 5000)) {
      return;
    }
    if (!send(out, "root")) {
      return;
    }
    if (!expect(in, "password: ", 5000)) {
      return;
    }
    if (!send(out, "password")) {
      return;
    }

这里是expect()send()方法,

  protected boolean expect(InputStream in, String s, long timeout) {
    ALog.i(this, "expecting: %s", s);

    final AtomicBoolean lock = new AtomicBoolean(false);
    final ExpectThread t = new ExpectThread(in, s, lock, timeout);
    t.start();
    synchronized (lock) {
      try {
        lock.wait(timeout);
      } catch (InterruptedException e) {
      }
    }
    t.interrupt();
    return lock.get();
  }

  protected boolean send(PrintWriter out, String s) {
    out.println(s);
    out.flush();
    ALog.i(this, "sent: %s", s);
    return true;
  }

这里是ExpectThread

  private class ExpectThread extends Thread {
    private final InputStream in;
    private final String expected;
    private final AtomicBoolean lock;
    private final long start;
    private final long timeout;

    ExpectThread(InputStream in, String expected, AtomicBoolean lock, long timeout) {
      this.in = in;
      this.expected = expected.toLowerCase();
      this.lock = lock;
      this.timeout = timeout;
      this.start = System.currentTimeMillis();
    }

    @Override
    public void run() {
      final StringBuilder b = new StringBuilder();
      final byte[] buffer = new byte[1024];
      int c;

      try {
        while (!isInterrupted() && System.currentTimeMillis() < start + timeout) {
          ALog.i(this, "starting read ...");
          while ((c = in.read(buffer)) != -1) {
            String s = new String(buffer, 0, c);
            b.append(s.toLowerCase());
            ALog.i(this, "read string: %s, buffer: %s", s, b.toString());
            if (b.toString().contains(expected)) {
              ALog.i(this, "found expected");
              lock.set(true);
              return;
            }
          }
          ALog.i(this, "waiting for read ...");
          SystemClock.sleep(1000);
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        synchronized (lock) {
          lock.notifyAll();
        }
      }
    }
  }

这是FAILED程序会议的wirehark pcap, https://drive.google.com/file/d/0B5iST80rpTN9c1RsRTNFaE5GZHM/view?usp=sharing

这是一个成功的终端(linux telnet客户端)会话的pcap, https://drive.google.com/file/d/0B5iST80rpTN9bDZFOHhkSHlPSE0/view?usp=sharing

我看到Linux客户端发送了一个&#34; WILL AUTHENTICATE&#34;,我的代码没有。如果我能弄明白如何让TelnetClient发送此类命令,我会尝试一下。

2 个答案:

答案 0 :(得分:2)

您必须明确发送\r\n,而不是println()。它必须正是这样,而不是系统上println()所做的任何事情。

我还怀疑你的expect()方法可能正在提前阅读。它应该一次读取一个字节或一个字符,以确保不会发生这种情况。请尝试使用套接字读取超时而不是Megillah的线程和锁定并等待:

protected boolean expect(Socket socket, InputStream in, String expected, long timeout) throws IOException {
    ALog.i(this, "expecting: %s", s);
    long endTime = System.currentTimeMillis() + timeout;
    byte[] buffer = new byte[8192];
    StringBuffer b = new StringBuffer();
    try
    {
        while (System.currentTimeMillis() < endTime)
        {
            socket.setSoTimeout((int)(endTime - System.currentTimeMillis()));
            ALog.i(this, "starting read ...");
            int c = in.read();
            if (c == -1)
            {
                return false;
            }
              ALog.i(this, "read string: %s, buffer: %s", s, b.toString());
            b.append(Character.valueOf((char)c));
            if (b.toString().toLowerCase().contains(expected))
            {
                ALog.i(this, "found expected");
                return true;
            }
        }
        return false;
    }
    catch (SocketTimeoutException exc)
    {
        return false;
    }
}

E&安培; OE

答案 1 :(得分:0)

回答我自己的问题......

正如我所怀疑的那样,这个特定服务器的telnet协商存在问题。我不得不使用一个非常具体的echo选项处理程序,

client.addOptionHandler(new EchoOptionHandler(false, false, false, true));

这意味着接受服务器正在发送的DO ECHO。我通过比较非工作会话和工作会话之间的telnet DO / WILL协商来发现这种差异。