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