dongqiao9583 2013-05-02 14:13
浏览 39
已采纳

PHP fread ssh stream显着慢

Scenario: I needed a function to get STDOUT of a command run through SSH asynchronously. This has various uses including (and most importantly) reading files through SSH. An important feature of this function is that it is asynchronous, hence I can display output as it is returned from the server (or give an estimate of the file download progress). This differs from the common approach of using ssh_move() to download files.

function ssh_exec($dsn, $cmd, $return=true, $size_est=null){
    $BUFFER_SIZE = $return ? (1024 * 1024) : 1;
    debug('ssh2_exec '.$cmd);
    $stream = ssh2_exec(ssh_open($dsn), $cmd);
    debug('stream_set_blocking');
    stream_set_blocking($stream, true);
    debug('ssh2_fetch_stream');
    $stream_out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
    stream_set_blocking($stream_out, true);
    debug('stream_get_contents');
    $data = ''; $stime = $oldtime = microtime(true); $data_len = 0;
    if(!$return){
        write_message("\033[0m".'  Execution Output:'.PHP_EOL.'    ');
    }
    while(!feof($stream_out)){
        $buff = fread($stream_out, $BUFFER_SIZE);
        if($buff===false)throw new Exception('Unexpected result from fread().');
        if($buff===''){
            debug('Empty result from fread()...breaking.');
            break;
        }
        $data .= $buff;
        if($return){
            $buff_len = strlen($buff);
            $data_len += $buff_len;
            $newtime = microtime(true);
            debugo('stream_get_contents '.bytes_to_human($data_len)
                .' @ '.bytes_to_human($buff_len / ($newtime - $oldtime)).'/s'
                .' ['.($size_est ? number_format($data_len / $size_est * 100, 2) : '???').'%]');
            $oldtime = $newtime;
        }else{
            echo str_replace(PHP_EOL, PHP_EOL.'    ', $buff);
        }
    }
    if($return){
        debugo('stream_get_contents Transferred '.bytes_to_human(strlen($data)).' in '.number_format(microtime(true) - $stime, 2).'s');
        return $data;
    }
}

Usage: The function is used like so:

$dsn = 'ssh2.exec://root:pass@host/';
$size = ssh_size($dsn, 'bigfile.zip');
$zip = ssh_exec($dsn, 'cat bigfile.zip', true, $size);

Note 1: An explanation of some non-standard functions:

  • debug($message) - Writes debug message to console.
  • ssh_open($dsn) - Takes in an SSH URI and returns an SSH connection handle.
  • bytes_to_human($bytes) - Converts the number of bytes to human readable format (eg: 6gb)
  • debugo($message) - The same as debug() but overwrites last line.

Note 2: The parameter $size_est is used in the progress indicator; usually you'd first get the file size and then attempt to download it (as in my example). It is optional so that it can be ignored when you just want to run an SSH command.

The Problem: Running the same download operation via scp root@host:/bigfile.zip ./, I get speeds up to 1mb/s whereas this script seems to limit to 70kb/s. I'd like to know why and how to improve this.

Edit: Also, I'd like to know how/if $BUFFER_SIZE does any difference.

  • 写回答

1条回答 默认 最新

  • drqrdkfue521903877 2013-05-02 15:25
    关注

    You should be able to use phpseclib, a pure PHP SSH implementation, to do this. eg.

    $ssh->exec('cat bigfile.zip', false);
    
    while (true) {
        $temp = $this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC);
        if (is_bool($temp)) {
            break;
        }
        echo $temp;
    }
    

    Might actually be faster too.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)