博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
100行PHP代码实现socks5代理服务器
阅读量:6286 次
发布时间:2019-06-22

本文共 3689 字,大约阅读时间需要 12 分钟。

hot3.png

前两天在B站上看到一个小伙纸100元组装个电脑打LOL画质流畅,突发奇想100行代码能(简单)实现个啥好玩的。我主要是做php开发的,于是就有了本文。

当然,由于php(不算swoole扩展)本身不擅长做网络服务端编程,所以这个代理,只是个玩具,离日常使用有点距离。如果想使用稳定可靠的加密(所以能禾斗学上网)代理,可以用这个: 也是100来行代码使用go实现。

写的过程中发现php多线程还是难的。比如我开始想每个连接新建一个线程。但这个线程得保存起来(比如保存到数组),比如官方例子中的这个: 要放到$clients这个数组里,不然,你试试(curl -L一个要301的地址)就知道出现什么情况了。

这个例子说了in the real world, do something here to ensure clients not running are destroyed 但是,如何把不再运行的连接销毁却没有讲。恩。我试了把$clients放到一个类里,把类传给线程类,然后在线程类要结束时把$clients里对应的连接给unset掉,无果。

那,以下就是使用线程池来实现的代理,按道理讲,退出时池要shutdown(),监听socket也要shutdown的,但百行代码,就不勉强了,随着ctrl + c,就让操作系统来回收资源吧。

php不擅长网络编程体现在哪里呢?首先我用的是stream_socket_XXX相关的函数,为啥不用socket扩展呢?因为socket扩展有问题,参见: 而stream_set_timeout对stream_socket_recvfrom这些高级操作,不起作用,参见: 而这些,在写代理时都需要考虑的。比如连接远程目标服务器时,没有超时控制,很容易就线程池跑满了。

测试的话,使用curl即可,对了,目前只支持远程dns解析,为啥呢?因为这个玩具后期可是要实现禾斗学上网的哟: curl --socks5-hostname 127.0.0.1:1080

Class Pipe extends Threaded{    private $client;    private $remote;    public function __construct($client, $remote)     {        $this->client = $client;        $this->remote = $remote;     }    public function run()    {        for ( ; ; ) {                $data = stream_socket_recvfrom($this->client, 4096);                if ($data === false || strlen($data) === 0) {                    break;                }                 $sendBytes = stream_socket_sendto($this->remote, $data);                if ($sendBytes <= 0) {                    break;                }        }        stream_socket_shutdown($this->client, STREAM_SHUT_RD);        stream_socket_shutdown($this->remote, STREAM_SHUT_WR);    }}Class Client extends Threaded{    public $fd;    public function __construct($fd)    {        $this->fd = $fd;     }    public function run()    {        $data = stream_socket_recvfrom($this->fd, 2);        $data = unpack('c*', $data);        if ($data[1] !== 0x05) {            stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);            echo '协议不正确.', PHP_EOL;            return;        }        $nmethods = $data[2];        $data = stream_socket_recvfrom($this->fd, $nmethods);        stream_socket_sendto($this->fd, "\x05\x00");        $data = stream_socket_recvfrom($this->fd, 4);        $data = unpack('c*', $data);        $addressType = $data[4];        if ($addressType === 0x03) { // domain            $domainLength = unpack('c', stream_socket_recvfrom($this->fd, 1))[1];            $data = stream_socket_recvfrom($this->fd, $domainLength + 2);            $domain = substr($data, 0, $domainLength);            $port = unpack("n", substr($data, -2))[1];        } else {            stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);            echo '请使用远程dns解析.', PHP_EOL;        }        stream_socket_sendto($this->fd, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00");        echo "{$domain}:{$port}", PHP_EOL;        $remote = stream_socket_client("tcp://{$domain}:{$port}");        if ($remote === false) {            stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);            return;        }        $pool = $this->worker->pipePool;        $pipe1 = new Pipe($remote, $this->fd);        $pipe2 = new Pipe($this->fd, $remote);        $pool->submit($pipe1);        $pool->submit($pipe2);    }}class ProxyWorker extends Worker{    public $pipePool;    public function __construct($pipePool)    {        $this->pipePool = $pipePool;    }}$server = stream_socket_server('tcp://0.0.0.0:1080', $errno, $errstr);if ($server === false)    exit($errstr);$pipePool = new Pool(200, Worker::class);$pool = new Pool(50, 'ProxyWorker', [$pipePool]);for( ; ; ) {    $fd = @stream_socket_accept($server, 60);    if ($fd === false)        continue;    $pool->submit(new Client($fd));}

转载于:https://my.oschina.net/u/142205/blog/666545

你可能感兴趣的文章
《锋利的SQL(第2版)》——1.7 常用函数
查看>>
jquery中hover()的用法。简单粗暴
查看>>
线程管理(六)等待线程的终结
查看>>
spring boot集成mongodb最简单版
查看>>
DELL EqualLogic PS存储数据恢复全过程整理
查看>>
《Node.js入门经典》一2.3 安装模块
查看>>
《Java 开发从入门到精通》—— 2.5 技术解惑
查看>>
Linux 性能诊断 perf使用指南
查看>>
实操分享:看看小白我如何第一次搭建阿里云windows服务器(Tomcat+Mysql)
查看>>
Sphinx 配置文件说明
查看>>
数据结构实践——顺序表应用
查看>>
python2.7 之centos7 安装 pip, Scrapy
查看>>
机智云开源框架初始化顺序
查看>>
Spark修炼之道(进阶篇)——Spark入门到精通:第五节 Spark编程模型(二)
查看>>
一线架构师实践指南:云时代下双活零切换的七大关键点
查看>>
ART世界探险(19) - 优化编译器的编译流程
查看>>
玩转Edas应用部署
查看>>
music-音符与常用记号
查看>>
sql操作命令
查看>>
zip 数据压缩
查看>>