程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> php無阻塞SSH客戶端實例

php無阻塞SSH客戶端實例

編輯:關於PHP編程

之前工作中必須和國外服務器打交道,延遲和丟包問題有時候非常嚴重,已經到了不可忍受的地步,輸入一條sql都是很費勁的事情。google搜了一遍沒有找到非阻塞的ssh客戶端,PHP有SSH2擴展,利用標准輸入輸出理論上可以實現一個基於命令的SSH客戶端,這樣就解決了網絡問題帶來的不便,於是開發了一個PHP非阻塞SSH客戶端

價值:

  • 基於命令,最大程度解決了網絡延遲和丟包問題。
  • windows和Linux下測試通過。

不足:

  • 沒有自動補全功能
  • 沒有sftp和scp等其他功能
  • 沒有顏色和粗體顯示
  • 個別情況下顯示上不是很完美
  • 因為現在基本不用它了,所以暫時先不進行改進。

linux 運行效果
2013-02-04_133311
windows下運行效果
2013-02-04_133509
2013-02-04_133727
2013-02-04_133842

因為是框架中的一個類,所以個別通用函數(比如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(empty($host['addr'])){
   debug_print('Host cant't be empty',E_USER_ERROR);
  }
  if(empty($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) || empty($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(!empty($out) and !empty($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(!empty($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();

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved