PHP内网穿透

  • A+
所属分类:PHP

此工具是纯PHP项目,基于GatewayWorkerworkerman编写而成

先看效果

https://player.bilibili.com/player.html?aid=577313164&bvid=BV1VB4y1o7WN&cid=1303068969&p=1

支持文件下载(只是大文件下载的会有点慢)

有三个文件,分别为server_listen、server_connect、local_connect

其中server_listen部署在服务器,开启一个端口监听来自server_connect客户端发来的请求,以及返回local_connect设备上的数据信息

server_listen、server_connect部署在服务器上,local_connect部署在需要暴漏在公网的计算机设备上

server_listen监听一个websocket服务

server_connect通过前端js连接server_listen所监听的端口

local_connect也连接server_listen所监听的端口

从而致使用户通过server_connect的web界面与local_connect所部署的计算机产生了连接

server_listen核心代码:

<?php

namespace plugin\webman\gateway;
use GatewayWorker\Lib\Gateway;
use think\facade\Cache;
use Workerman\Connection\TcpConnection;
class Events{
   public static $initialize_path="D:\\Desktop\\";//初始化路径

    public static function onWorkerStart($worker){

        TcpConnection::$defaultMaxPackageSize = 2024000000;//2G


    }

    public static function onConnect($client_id){


    }

    public static function onWebSocketConnect($client_id, $data){


    }

    public static function onMessage($client_id, $message){


            $message=json_decode($message,true);
            $type=$message['type'];
            switch ($type){
                case "connect":
                    //来自1、2的消息连接绑定
                    Gateway::bindUid($client_id,$message['id']);
                    break;


                case "open_path":
                    //来自1要打开路径的消息
                    Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>$message['data']]));//发送给2
                    break;

                case "return_open_path":
                    //来自2的路径及文件信息回显
                    $now_path=$message['data']['now_path'];
                    Cache::set("now_path",$now_path);
                    Gateway::sendToUid(1,json_encode(["type"=>"show_path","data"=>$message['data']]));//发送给1
                    break;

                case "back":
                    //来自1发送的路径后退请求

                    $now_path=Cache::get("now_path");//获取当前路径

                    if ($now_path!=Events::$initialize_path){
                        //当前路径不等于初始化路径时可以退

                    Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>dirname($now_path,1)]));
                    }
                    break;


                /*
                    服务器为Linux请使用如下代码,以为Linux与Windows路径不同
                    case "back":
                    //来自1发送的路径后退请求

                    $now_path=Cache::get("now_path");//获取当前路径
                    $now_path = str_replace('\\','/',$now_path);
                    $initialize_path=Events::$initialize_path;
                    $initialize_path=str_replace('\\','/',$initialize_path);

                    if ($now_path!=$initialize_path){
                        //当前路径不等于初始化路径时可以退

                        $go_path=dirname($now_path,1);
                        $go_path=str_replace('/','\\',$go_path);

                        Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>$go_path]));
                    }
                    break;
                */

                case "download":
                    //来自1的下载请求
                    Gateway::sendToUid(2,json_encode(["type"=>"download","data"=> $message['data']]));//发送给2获取下载资源
                    break;

                case "return_download":
                    //来自2返回的下载资源base64编码
                    Gateway::sendToUid(1,json_encode(["type"=>"return_download","data"=>$message['data']]));//发送给1
                    break;
            }






    }

    public static function onClose($client_id){

    }

}

server_connect核心代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script "https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<p onclick="back()">&lt;后退</p>
<p id="now_path"></p>
<div id="box">

</div>



<script>
    // 假设服务端ip为127.0.0.1
    ws = new WebSocket("ws://127.0.0.1:7272");
    var id=1;
    var now_path;
    ws.onopen = function() {

        var arr={"type":"connect","id":id}
       ws.send(JSON.stringify(arr))

    };
    ws.onmessage = function(e) {
        var data= JSON.parse(e.data)
        var type=data.type
        switch (type){
            case "show_path":

                var d=data.data
                 now_path=d.now_path
                $("#box").html("")//重置一下
                $("#now_path").html("当前路径为:"+now_path)
                if (d.dir){

                    for (var i=0;i<d.dir.length;i++){
                        var url=d.dir[i]
                        var encodeUrl=encodeURI(d.dir[i])//转义一下防止路径斜杠被转义
                        $("#box").append("<p onclick="+'open_path("'+encodeUrl+'")'+" style='color: red'>(文件夹)"+url+"</p>")
                    }
                }
                if (d.file){
                    for (var s=0;s<d.file.length;s++){
                        var url=d.file[s]
                        var encodeUrl=encodeURI(d.file[s])//转义一下防止路径斜杠被转义
                        $("#box").append("<p onclick="+'download("'+encodeUrl+'")'+" style='color: blue'>(文件)"+url+"</p>")

                    }
                }

                break;


            case "return_download":

                var base64=data.data.txt
                var file_name=data.data.file_name

                $.ajax({
                    url:'{:route("download")}',
                    type: "POST",
                    data:{txt:base64,file_name},
                    success: function(data) {
                        alert("下载完毕")
                        console.log(data)
                         window.open('{:route("get_file")}'+'?file_path='+data.file_path+'&file_name='+data.file_name,'_blank')
                    }

                });//解析下载资源


                break;

        }



    };

    //打开指定路径
    function open_path(data){
        //console.log("方法在打印"+decodeURI(data))//解码
        var arr={"type":"open_path","data":decodeURI(data)}
        ws.send(JSON.stringify(arr))
    }

    //下载指定文件
    function download(data){
        //console.log("方法在打印"+decodeURI(data))//解码
        console.log("正在下载:"+decodeURI(data))
        console.log("大文件可能下载较慢请稍等")
        var arr={"type":"download","data":decodeURI(data)}
        ws.send(JSON.stringify(arr))
    }

    //后退
    function back(){
        var arr={"type":"back"}
        ws.send(JSON.stringify(arr))
    }
</script>
</body>
</html>

local_connect核心代码

<?php
ini_set('memory_limit', '2024M');
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
TcpConnection::$defaultMaxSendBufferSize = 2024000000;
$worker = new Worker();


$worker->onWorkerStart = function($worker){

    $con = new AsyncTcpConnection('ws://127.0.0.1:7272');


    // websocket握手成功后
    $con->onWebSocketConnect = function(AsyncTcpConnection $con) {
        $con->send(json_encode(["type"=>"connect","id"=>2]));//发起连接请求
        /*
         *这里的路径需要与server_listen/plugin/webman/gateway/Events.php
         * 里的$initialize_path相关联,$initialize_path比这里的路径多\\
         */
        $con->send(json_encode(["type"=>"return_open_path","data"=>get_dir("D:\\Desktop")]));//发送初始化路径
    };

    // 当收到消息时
    $con->onMessage = function(AsyncTcpConnection $con, $data) {


            $data=json_decode($data,true);
                $type=$data['type'];

            switch ($type){

                case "open_path":
                    //来自服务器转发的打开路径
                    $dir=$data['data'];
                    $con->send(json_encode(["type"=>"return_open_path","data"=>get_dir($dir)]));

                break;

                case "download":
                    //响应服务器的下载请求
                     $file_path=$data['data'];
                        $pattern = '/[^\\\\]*$/'; // 注意正则表达式中的双反斜杠,因为PHP需要转义反斜杠
                        if (preg_match($pattern, $file_path, $matches)) {
                            $file_name = $matches[0];
                        }
                     $txt=file_get_contents($file_path);
                     $txt=base64_encode($txt);
                     $con->send(json_encode(["type"=>"return_download","data"=>["txt"=>$txt,"file_name"=>$file_name]]));

                    break;


            }



    };

    function get_dir($path) {
        $list=[];
        $list['dir']=[];
        $list['file']=[];
        $currentDir = opendir($path);
        //opendir()返回一个目录句柄,失败返回false
        while(($file = readdir($currentDir)) !== false) {
            //readdir()返回打开目录句柄中的一个条目
            $subDir = $path . DIRECTORY_SEPARATOR . $file;
            //构建子目录路径
             if(is_dir($subDir)) {
                //如果是目录,进行递归
                if (!preg_match("/\.\.?$/",$subDir)){
                    array_push($list['dir'],$subDir);//将文件目录中\.或\..文件不添加
                }

            } else {
                //如果是文件,调用clasbackFun方法(参数:文件路径,文件名)
                 array_push($list['file'],$subDir);
            }
        }
        $list['now_path']=$path. DIRECTORY_SEPARATOR;
        return $list;
    }







    $con->connect();
};



Worker::runAll();

内网穿透下载

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: