首页 > php安装 > php无阻塞SSH客户端实例

php无阻塞SSH客户端实例

之前工作中必须和国外服务器打交道,延迟和丢包问题有时候非常严重,已经到了不可忍受的地步,输入一条sql都是很费劲的事情,google搜了一遍没有找到非阻塞的ssh客户端,PHP有SSH2扩展,利用标准输入输出理论上可以实现一个基于命令的SSH客户端,这样就解决了网络问题带来的不便,于是开发了一个PHP非阻塞SSH客户端.

价值:基于命令,最大程度解决了网络延迟和丢包问题,windows和Linux下测试通过.

不足:没有自动补全功能,没有sftp和scp等其他功能,没有颜色和粗体显示,个别情况下显示上不是很完美,因为现在基本不用它了,所以暂时先不进行改进.

因为是框架中的一个类,所以个别通用函数(比如debug_print())需要自己提供,我这里就不改写了,代码如下:

<?php
class FSSH {
    private $conn;
    private $shell;
    /** 
     * key=String 密码认证,key=array('pub'=>,'pri'=>,'type'=>,'phrase'=>)密钥认证
     * 密钥认证type分为两种:ssh-rsa,ssh-dss
     * $host[addr]=String 地址,$host['fp']=array() 服务器指纹
     */
    function __construct($host, $user, $key) {
        if (emptyempty($host['addr'])) {
            debug_print('Host cant\'t be emptyempty', E_USER_ERROR);
        }
        if (emptyempty($host['fp'])) {
            debug_print('finger print is not specified', E_USER_ERROR);
        }
        $this->stdin = fopen('php://stdin', 'r');
        $this->stdout = fopen('php://stdout', 'w');
        if (false !== strpos($host['addr'], ':')) {
            $temp = explode(':', $host['addr']);
            $host['addr'] = $temp[0];
            $port = $temp[1];
        } else {
            $port = 22;
        }
        if (is_string($key) || emptyempty($key['type'])) {
            $methods = null;
        } else {
            $methods = array(
                'hostkey' => $key['type']
            );
        }
        $conn = ssh2_connect($host['addr'], $port, $methods, array(
            'disconnect' => array(
                $this,
                'disconnect'
            )
        ));
        $fp = ssh2_fingerprint($conn, SSH2_FINGERPRINT_MD5);
        $success = false;
        $fpOK = false;
        if (in_array($fp, $host['fp'])) {
            $fpOK = true;
        } else {
            fwrite($this->stdout, "$fpnIs fingerprint OK ?(y/n)");
            $input = strtolower(stream_get_line($this->stdin, 1));
            if ($input == 'y') {
                $fpOK = true;
            } else {
                $fpOK = false;
            }
        }
        if ($fpOK) {
            if (is_array($key)) {
                if (ssh2_auth_pubkey_file($conn, $user, $key['pub'], $key['pri'], $key['phrase'])) {
                    $success = true;
                } else {
                    debug_print('Public Key Authentication Failed', E_USER_ERROR);
                }
            } elseif (is_string($key)) {
                if (ssh2_auth_password($conn, $user, $key)) {
                    $success = true;
                } else {
                    debug_print('Password Authentication Failed', E_USER_ERROR);
                }
            }
        } else {
            debug_print('Fingerprint is invalid', E_USER_ERROR);
        }
        if ($success) {
            $this->conn = $conn;
            $this->shell = ssh2_shell($conn, null, null, 1024);
        }
        return $success;
    }
    function shell() {
        //最后一条命令
        $last = '';
        //先结束shell,再结束while
        $signalTerminate = false;
        while (true) {
            $cmd = $this->fread($this->stdin);
            $out = stream_get_contents($this->shell, 1024);
            if (!emptyempty($out) and !emptyempty($last)) {
                $l1 = strlen($out);
                $l2 = strlen($last);
                $l = $l1 > $l2 ? $l2 : $l1;
                $last = substr($last, $l);
                $out = substr($out, $l);
            }
            echo ltrim($out);
            if ($signalTerminate) {
                break;
            }
            if (in_array(trim($cmd) , array(
                'exit'
            ))) {
                $signalTerminate = true;
            }
            if (!emptyempty($cmd)) {
                $last = $cmd;
                fwrite($this->shell, $cmd);
            }
        }
    }
    //解决windows命令行的读取问题,没有别的办法了。
    private function fread($fd) {
        static $data = '';
        $read = array(
            $fd
        );
        $write = array();
        $except = array();
        $result = stream_select($read, $write, $except, 0, 1000);
        if ($result === false) debug_print('stream_select failed', E_USER_ERROR);
        if ($result !== 0) {
            $c = stream_get_line($fd, 1);
            if ($c != chr(13)) $data.= $c;
            if ($c == chr(10)) {
                $t = $data;
                $data = '';
                return $t;
            }
        }
    }
    function __destruct() {
        fclose($this->stdin);
        fclose($this->stdout);
        $this->disconnect();
    }
    private function disconnect() {
        if (is_resource($this->conn)) {
            unset($this->conn);
            fclose($this->shell);
        }
    }
}
?>

demo,代码如下:

//$ssh=new FSSH(array('addr'=>'x.x.x.x:22','fp'=>array('')),'tunnel',array('pub'=>'E:Identity.pub','pri'=>'E:Identity','type'=>'ssh-rsa')); 
$ssh=new FSSH(array('addr'=>'192.168.2.205','fp'=>array('54ECC700B844DCF0D40554A56C57C01E')),'root','123456'); 
$ssh->shell();


文章地址:http://www.phprm.com/install/fs4780.html

转载随意^^请带上本文地址!

标签:php客户端 ssh客户端

相关文章

发表留言