简体   繁体   English

“传输协议线程失败” – “套接字是 EOF”,使用 J2SSH 连接使用 Java

[英]"The Transport Protocol thread failed" – "The socket is EOF" with J2SSH connection using Java

I am trying to establish an SSH connection through my Java code, but getting below exception.. I tested my connection through Putty/Winscp tools and it works fine.我正在尝试通过我的 Java 代码建立一个 SSH 连接,但是遇到异常。我通过 Putty/Winscp 工具测试了我的连接,它工作正常。 The problem is with my Java code...问题在于我的 Java 代码...

SEVERE: The Transport Protocol thread failed
java.io.IOException: The socket is EOF
    at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readBufferedData(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolInputStream.readMessage(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.readMessage(Unknown Source)
    at com.sshtools.j2ssh.transport.kex.DhGroup1Sha1.performClientExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolClient.performKeyExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.beginKeyExchange(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.onMsgKexInit(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.startBinaryPacketProtocol(Unknown Source)
    at com.sshtools.j2ssh.transport.TransportProtocolCommon.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

Below is my piece of Java code to establish the connection下面是我的一段Java代码建立连接

public class MySSHClient {

  static SshClient ssh = null;
  static SshConnectionProperties properties = null;
  SessionChannelClient session = null;

  private static void MySSHClient(String hostName, String userName, String passwd )
  {

    try
    {
      // Make a client connection
      ssh = new SshClient();
      properties = new SshConnectionProperties();
      properties.setHost("192.168.1.175");

      // Connect to the host
      ssh.connect(properties, new IgnoreHostKeyVerification());

      // Create a password authentication instance
      PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();

      pwd.setUsername("root");
      pwd.setPassword("123456");

      // Try the authentication
      int result = ssh.authenticate(pwd);

      // Evaluate the result
      if (result==AuthenticationProtocolState.COMPLETE) {

        System.out.println("Connection Authenticated");
      }
    }
    catch(Exception e)
    {
      System.out.println("Exception : " + e.getMessage());
    }

  }//end of method.


  public String execCmd(String cmd)
  {
    String theOutput = "";
    try
    {
      // The connection is authenticated we can now do some real work!
      session = ssh.openSessionChannel();

      if ( session.executeCommand(cmd) )
      {
        IOStreamConnector output = new IOStreamConnector();
        java.io.ByteArrayOutputStream bos =  new
        java.io.ByteArrayOutputStream();
        output.connect(session.getInputStream(), bos );
        session.getState().waitForState(ChannelState.CHANNEL_CLOSED);
        theOutput = bos.toString();
      }
      //else
      //throw Exception("Failed to execute command : " + cmd);
      //System.out.println("Failed to execute command : " + cmd);
    }
    catch(Exception e)
    {
      System.out.println("Exception : " + e.getMessage());
    }

    return theOutput;
  }

  public static void main(String[] args){
      MySSHClient(null, null, null);
    }

Motivation动机

I stumbled across this question and answers while investigating the error in question java.io.IOException: The socket is EOF .我在调查问题java.io.IOException: The socket is EOF的错误时偶然发现了这个问题和答案。 Because changing the code to use some other SSH Java library is not immediately possible in my case and the stated explanation by @a3.14_Infinity was not detailed enough for me, I'd like to add my take on it.因为在我的情况下不能立即更改代码以使用其他一些 SSH Java 库,而且@a3.14_Infinity 的说明对我来说不够详细,我想补充一下我的看法。

java.io.IOException: The socket is EOF - Why? java.io.IOException: 套接字是 EOF - 为什么?

Because this exception is not very helpful, I first tried Wireshark to see what's going on over the wire, but to no avail.因为这个异常不是很有帮助,所以我首先尝试了Wireshark来查看网络上发生了什么,但无济于事。 So I configured the sshd_config (OpenSSH 6.9) to log on DEBUG3 level and got the answer in the /var/log/auth.log file of my test machine.所以我配置了sshd_config (OpenSSH 6.9)登录DEBUG3级别,在我测试机的/var/log/auth.log文件中得到了答案。 It stated a fatal error while trying to negotiate the key exchange algorithm with the SSH client (the Java SSH library).它在尝试与 SSH 客户端(Java SSH 库)协商密钥交换算法时报告了一个致命错误

Because the SSH server and client could not agree on a mutual key exchange algorithm the OpenSSH server terminates the connection to the client.由于 SSH 服务器和客户端无法就相互密钥交换算法达成一致,OpenSSH 服务器终止与客户端的连接。 In consequence, the Java SSH library code throws the exception.结果,Java SSH 库代码抛出异常。

But why does it happen?但为什么会发生呢?

The sshtools.j2ssh ( sshtools : j2ssh-core : 0.2.9 ) library code is pretty old and discontinued. sshtools.j2ssh ( sshtools : j2ssh-core : 0.2.9 ) 库代码很旧并且已停产。 Starting with OpenSSH 6.7 (released October, 2014) default ciphers and MAC have been altered to remove unsafe algorithms which includes the blowfish-cbc cipher.OpenSSH 6.7 (2014 年 10 月发布)开始,默认密码和 MAC 已被更改,以删除包括blowfish-cbc密码在内的不安全算法。 And with OpenSSH 6.9 (released June, 2015) the support for the 1024-bit diffie-hellman-group1-sha1 key exchange is disabled by default.对于OpenSSH 6.9 (2015 年 6 月发布),默认情况下禁用对 1024 位diffie-hellman-group1-sha1密钥交换的支持。

When you still use the prehistoric SSH Tools j2ssh library (God forbid) connecting to a newer OpenSSH server you will get the described error.当您仍然使用史前 SSH 工具j2ssh库(上帝保佑)连接到较新的 OpenSSH 服务器时,您将收到所描述的错误。 The library code only offers the diffie-hellman-group1-sha1 key exchange algorithm to the OpenSSH server which it does not support by default.库代码仅向 OpenSSH 服务器提供diffie-hellman-group1-sha1密钥交换算法,默认情况下不支持。 Thus, a secure connection cannot be established.因此,无法建立安全连接。

Cannot change the code?不能改代码?

If moving to another Java SSH library is not immediately possible (my case) then you can re-enable the disabled diffie-hellman-group1-sha1 key exchange algorithm in the OpenSSH's server config file sshd_config .如果无法立即迁移到另一个 Java SSH 库(我的情况),那么您可以在 OpenSSH 的服务器配置文件sshd_config重新启用禁用的diffie-hellman-group1-sha1密钥交换算法。 For example like this.例如像这样。

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,blowfish-cbc

KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha1

But let me be clear on this.但让我清楚这一点。 The diffie-hellman-group1-sha1 key exchange algorithm as well as the blowfish-cbc cipher are turned off by default because they are insecure.默认情况下, diffie-hellman-group1-sha1密钥交换算法和blowfish-cbc密码是关闭的,因为它们不安全。 Reenabling them should only be a temporary measure until you can replace this obsolete Java SSH library.重新启用它们应该只是一个临时措施,直到您可以替换这个过时的 Java SSH 库。

Finally, I like to point out that the suggested Java Secure Channel (JSch) library in other answers is discontinued.最后,我想指出其他答案中建议的Java 安全通道 (JSch)库已停止使用。 So, you might want to consider sshj or even ssh2j-maverick instead.因此,您可能需要考虑sshj甚至ssh2j-maverick

Edit: I was wrong , the Java Secure Channel JSch library is alive (JSCH 0.1.54 was released on 2016-09-03 on MavenCentral ) and certainly worth your consideration.编辑:我错了Java 安全通道JSch库还活着(JSCH 0.1.54 于 2016 年 9 月 3日在 MavenCentral 上发布),当然值得您考虑。 Alternatively, you may want to consider also sshj or ssh2j-maverick .或者,您可能还想考虑sshjssh2j-maverick

Addendum: Migration附录:迁移

To keep the migration effort for the sshtools.j2ssh ( sshtools : j2ssh-core : 0.2.9 ) library minimal I looked at the commercial legacy SSH client library from SSHTOOLS (version 1.7.1).为了使sshtools.j2ssh ( sshtools : j2ssh-core : 0.2.9 ) 库的迁移工作最小化,我查看了来自SSHTOOLS (版本 1.7.1)的商业遗留 SSH 客户端库。 This allowed to keep the existing library integration code with few minor changes regarding library API and exception handling.这允许保留现有的库集成代码,对库 API 和异常处理进行少量更改。 Thus, if you do not want to restart from scratch then biting the bullet and sticking with SSHTOOLS is probably your best option.因此,如果您不想从头开始,那么咬紧牙关坚持使用 SSHTOOLS 可能是您最好的选择。 Finally, to gauge the migration effort I first replaced the library with SSHTOOLS' open source library ssh2j-maverick which almost has the same API as its latest commercial version (version 1.7.1).最后,为了衡量迁移工作,我首先用 SSHTOOLS 的开源库ssh2j-maverick替换了该库,该库几乎具有与其最新商业版本(版本 1.7.1)相同的 API。

This error ( "The Transport Protocol thread failed. java.io.IOException: The socket is EOF” ) occurs when j2ssh.jar file is not compatible with current SSH version of SFTP server.当 j2ssh.jar 文件与 SFTP 服务器的当前 SSH 版本不兼容时,会发生此错误( “传输协议线程失败。java.io.IOException :套接字为 EOF” )。

You can try using Java Secure Channel (JSch) from here .您可以从这里尝试使用 Java 安全通道 (JSch)。

Courtesy: http://techydiary.com/the-transport-protocol-thread-failed-java-io-ioexception-the-socket-is-eof/礼貌: http : //techydiary.com/the-transport-protocol-thread-failed-java-io-ioexception-the-socket-is-eof/

The following sample Code may help you,以下示例代码可能对您有所帮助,

import java.io.InputStream;
import org.apache.commons.io.IOUtils;


import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;


public class SSHClient {

    /**
     * Constant EXCUTE_CHANNEL
     */
    public static final String EXCUTE_CHANNEL = "exec";

    /**
     * Constant STRICT_KEY_CHECKING
     */
    public static final String STRICT_KEY_CHECKING = "StrictHostKeyChecking";


    /** Name/ip of the remote machine/device **/ 
    private String host;
    private String userName;
    private String password;

    /**
     * This method used to initilze user and host
     * 
     * @param userName
     * @param password
     * @param host
     */
    public SSHClient(String userName,String password, String host) {
        super();
        this.userName = userName;
        this.password = password;
        this.host = host;
    }

    /**
     * This method used to execute commands remotly by using SSHV2
     * 
     * @param host
     * @param username
     * @param password
     * @param command
     * @return
     */
    public String executeCommand(String command) { 
        StringBuilder log = new StringBuilder();
        String response = null;
        Channel channel = null;
        Session session = null;

        try {
            JSch jsch = new JSch();
            JSch.setConfig(STRICT_KEY_CHECKING, Constants.NO);

            session = jsch.getSession(userName, host, 22);


            // If two machines have SSH passwordless logins setup, the following
            // line is not needed:
            session.setPassword(password);
            session.connect();

            channel = session.openChannel(EXCUTE_CHANNEL);
            ((ChannelExec) channel).setCommand(command);

            // channel.setInputStream(System.in);
            channel.setInputStream(null);

            ((ChannelExec) channel).setErrStream(System.err);

            InputStream in = channel.getInputStream();

            channel.connect();

            response = IOUtils.toString(in);

        } catch (Exception ex) {
                //handle exception
        } finally {
            try {
                if (session != null) {
                    session.disconnect();
                }
            } catch (Exception ex) {
                //handle exception
            }
            try {
                if (channel != null) {
                    channel.disconnect();
                }
            } catch (Exception ex) {
                //handle exception
            }

        }
        System.ou.println( "Response received :"+  response));
        return response;
    }
}

Here is the working code reused from some google source -这是从一些谷歌源代码重用的工作代码 -

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.StreamGobbler;
Connection conn = new Connection(server);
conn.connect();
boolean isAuthenticated = conn.authenticateWithPassword(user_id, password);

System.out.println("Is server - " + server + " Authenticated? " + isAuthenticated);

if (isAuthenticated == false)
    throw new IOException("Authentication failed.");

ch.ethz.ssh2.Session sess = conn.openSession();
String new_commands = "";

for (int i = 0; i < commands.size(); i++) {
    new_commands = new_commands + commands.get(i) + "\n";
}

System.out.println("The command executed is: " + new_commands);

sess.requestDumbPTY();
sess.execCommand(new_commands);
InputStream stdout = new StreamGobbler(sess.getStdout());
BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
InputStream errStrm = new StreamGobbler(sess.getStderr());
BufferedReader stderrRdr = new BufferedReader(new InputStreamReader(errStrm));
sess.getStdin().write("EXIT\n".getBytes());

System.out.println("the output of the command is");

while (true) {
    String line_out = br.readLine();
    if (line_out == null) {
        break;
    } else {
        System.out.println(line_out);
        output_logs.add(line_out);
    }
}

while (true) {
    String line_error = stderrRdr.readLine();
    if (line_error == null) {
        break;
    } else {
        System.out.println(line_error);
        output_logs.add(line_error);
    }
}

output_logs.add("Exit Code:" + sess.getExitStatus());
System.out.println("ExitCode: " + sess.getExitSignal());

sess.close();
conn.close();

found a simple solution on the OS: comment out the Cipher line in /etc/ssh/sshd_config and run service sshd restart在操作系统上找到了一个简单的解决方案:注释掉 /etc/ssh/sshd_config 中的 Cipher 行并运行 service sshd restart

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM