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
转载随意^^请带上本文地址!