class SSHSession {

        private String address;
        private ClientSession clientSession;

        private SSHSession(ClientSession clientSession, String address) {
            this.clientSession = clientSession;
            this.address = address;
        }

        /**
         * 执行命令并返回结果，可以执行多次
         * @param cmd
         * @return 执行成功Result为true，并携带返回信息,返回信息可能为null
         *         执行失败Result为false，并携带失败信息
         *         执行异常Result为false，并携带异常
         */
        public Result executeCommand(String cmd) {
            return executeCommand(cmd, OP_TIMEOUT);
        }

        public Result executeCommand(String cmd, int timoutMillis) {
            return executeCommand(cmd, null, timoutMillis);
        }

        public Result executeCommand(String cmd, LineProcessor lineProcessor) {
            return executeCommand(cmd, lineProcessor, OP_TIMEOUT);
        }

        /**
         * 执行命令并返回结果，可以执行多次
         * @param cmd
         * @param lineProcessor 回调处理行
         * @return 如果lineProcessor不为null,那么永远返回Result.true
         */
        public Result executeCommand(String cmd, LineProcessor lineProcessor, int timoutMillis) {
            try (ByteArrayOutputStream stdout = new ByteArrayOutputStream();
                 ByteArrayOutputStream stderr = new ByteArrayOutputStream();
                 ClientChannel channel = clientSession.createExecChannel(cmd)) {
                channel.setOut(stdout);
                channel.setErr(stderr);
                channel.open().verify(timoutMillis);
                // Wait (forever) for the channel to close - signalling command finished
                channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);
                LineProcessor tmpLP = lineProcessor;
                // 如果客户端需要进行行处理，则直接进行回调
                if (tmpLP != null) {
                    processStream(new ByteArrayInputStream(stdout.toByteArray()), tmpLP);
                } else {
                    StringBuilder buffer = new StringBuilder();
                    tmpLP = generateDefaultLineProcessor(buffer);
                    processStream(new ByteArrayInputStream(stdout.toByteArray()), tmpLP);
                    if (buffer.length() > 0) {
                        return new Result(true, buffer.toString());
                    }
                }
                if(tmpLP.lineNum() == 0) {
                    // 返回为null代表可能有异常，需要检测标准错误输出，以便记录日志
                    Result errResult = tryLogError(new ByteArrayInputStream(stderr.toByteArray()), cmd);
                    if (errResult != null) {
                        return errResult;
                    }
                }
                return new Result(true, null);
            } catch (Exception e) {
                logger.error("execute ip:{} cmd:{}", address, cmd, e);
                return new Result(e);
            }
        }

        private Result tryLogError(InputStream is, String cmd) {
            StringBuilder buffer = new StringBuilder();
            LineProcessor lp = generateDefaultLineProcessor(buffer);
            processStream(is, lp);
            String errInfo = buffer.length() > 0 ? buffer.toString() : null;
            if (errInfo != null) {
                logger.error("address " + address + " execute cmd:({}), err:{}", cmd, errInfo);
                return new Result(false, errInfo);
            }
            return null;
        }

        /**
         * Copy a set of local files to a remote directory, uses the specified mode when
         * creating the file on the remote side.
         * @param localFiles
         *            Path and name of local file.
         * @param remoteFile
         *            name of remote file.
         * @param remoteTargetDirectory
         *            Remote target directory. Use an empty string to specify the default directory.
         * @param mode
         *            a four digit string (e.g., 0644, see "man chmod", "man open")
         * @throws IOException
         */
        public Result scp(String[] localFiles, String remoteFile, String remoteTargetDirectory, String mode) {<FILL_FUNCTION_BODY>}

        public Result scpToDir(String localFile, String remoteTargetDirectory) {
            return scpToDir(localFile, remoteTargetDirectory, "0744");
        }

        public Result scpToDir(String localFile, String remoteTargetDirectory, String mode) {
            return scp(new String[] { localFile }, null, remoteTargetDirectory, mode);
        }

        public Result scpToDir(String[] localFile, String remoteTargetDirectory) {
            return scp(localFile, null, remoteTargetDirectory, "0744");
        }
        public Result scpToFile(String localFile, String remoteFile, String remoteTargetDirectory) {
            return scpToFile(localFile, remoteFile, remoteTargetDirectory, "0744");
        }
        public Result scpToFile(String localFile, String remoteFile, String remoteTargetDirectory, String mode) {
            return scp(new String[] { localFile }, remoteFile, remoteTargetDirectory, "0744");
        }
    }

            try {
                ScpClient client = ScpClientCreator.instance().createScpClient(clientSession);
                String separator = FileSystems.getDefault().getSeparator();
                if(localFiles.length == 1){
                    if(StringUtils.isBlank(remoteFile)){
                        client.upload(localFiles, remoteTargetDirectory, ScpClient.Option.TargetIsDirectory);
                        int index = localFiles[0].lastIndexOf(separator);
                        if(index <= 0){
                            index = 0;
                        }else{
                            index = index + 1;
                        }
                        String fileName = localFiles[0].substring(index);
                        clientSession.executeRemoteCommand("chmod " + mode + " \"" + remoteTargetDirectory + "/" + fileName + "\"");
                    } else {
                        client.upload(localFiles, remoteTargetDirectory + "/" + remoteFile);
                        clientSession.executeRemoteCommand("chmod " + mode + " \"" + remoteTargetDirectory + "/" + remoteFile + "\"");
                    }
                } else {
                    client.upload(localFiles, remoteTargetDirectory, ScpClient.Option.TargetIsDirectory);
                    StringBuffer sb = new StringBuffer();
                    List<String> files = Arrays.asList(localFiles);
                    String remoteFiles = files.stream().map(file -> {
                        int index = file.lastIndexOf(separator);
                        if(index <= 0){
                            index = 0;
                        }else{
                            index = index + 1;
                        }
                        return " \"" + remoteTargetDirectory + "/" + file.substring(index) + "\"";
                    }).collect(Collectors.joining(" "));
                    clientSession.executeRemoteCommand("chmod " + mode + " " + remoteFiles);
                }
                return new Result(true);
            } catch (Exception e) {
                logger.error("scp local="+Arrays.toString(localFiles) + " to " +
                        remoteTargetDirectory + " remote=" + remoteFile + " err", e);
                return new Result(e);
            }
        
<no_super_class>