当前位置:首页 > PHP教程 > php高级应用 > 列表

100行PHP代码实现socks5代理服务器

发布:smiling 来源: PHP粉丝网  添加日期:2021-07-31 20:19:41 浏览: 评论:0 

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

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

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

写的过程中发现php多线程还是难的。比如我开始想每个连接新建一个线程。但这个线程得保存起来(比如保存到数组),比如官方例子中的这个:https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.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扩展有问题,参见:https://github.com/krakjoe/pthreads/issues/581 而stream_set_timeout对stream_socket_recvfrom这些高级操作,不起作用,参见:http://php.net/manual/en/function.stream-set-timeout.php 而这些,在写代理时都需要考虑的,比如连接远程目标服务器时,没有超时控制,很容易就线程池跑满了。

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

  1. Class Pipe extends Threaded 
  2.   private $client
  3.   private $remote
  4.   public function __construct($client$remote)  
  5.   { 
  6.     $this->client = $client
  7.     $this->remote = $remote;  
  8.   } 
  9.   public function run() 
  10.   { 
  11.     for ( ; ; ) { 
  12.         $data = stream_socket_recvfrom($this->client, 4096); 
  13.         if ($data === false || strlen($data) === 0) { 
  14.           break
  15.         }  
  16.         $sendBytes = stream_socket_sendto($this->remote, $data); 
  17.         if ($sendBytes <= 0) { 
  18.           break
  19.         } 
  20.     } 
  21.     stream_socket_shutdown($this->client, STREAM_SHUT_RD); 
  22.     stream_socket_shutdown($this->remote, STREAM_SHUT_WR); 
  23.   } 
  24.  
  25. Class Client extends Threaded 
  26.   public $fd
  27.   public function __construct($fd
  28.   { 
  29.     $this->fd = $fd;  
  30.   } 
  31.  
  32.   public function run() 
  33.   { 
  34.     $data = stream_socket_recvfrom($this->fd, 2); 
  35.     $data = unpack('c*'$data); 
  36.     if ($data[1] !== 0x05) { 
  37.       stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR); 
  38.       echo '协议不正确.', PHP_EOL; 
  39.       return
  40.     } 
  41.     $nmethods = $data[2]; 
  42.     $data = stream_socket_recvfrom($this->fd, $nmethods); 
  43.     stream_socket_sendto($this->fd, "\x05\x00"); 
  44.     $data = stream_socket_recvfrom($this->fd, 4); 
  45.     $data = unpack('c*'$data); 
  46.     $addressType = $data[4]; 
  47.     if ($addressType === 0x03) { // domain 
  48.       $domainLength = unpack('c', stream_socket_recvfrom($this->fd, 1))[1]; 
  49.       $data = stream_socket_recvfrom($this->fd, $domainLength + 2); 
  50.       $domain = substr($data, 0, $domainLength); 
  51.       $port = unpack("n"substr($data, -2))[1]; 
  52.     } else { 
  53.       stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR); 
  54.       echo '请使用远程dns解析.', PHP_EOL; 
  55.     } 
  56.  
  57.     stream_socket_sendto($this->fd, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00"); 
  58.     echo "{$domain}:{$port}", PHP_EOL; 
  59.     $remote = stream_socket_client("tcp://{$domain}:{$port}"); 
  60.     if ($remote === false) { 
  61.       stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR); 
  62.       return
  63.     } 
  64.  
  65.     $pool = $this->worker->pipePool; 
  66.  
  67.     $pipe1 = new Pipe($remote$this->fd); 
  68.     $pipe2 = new Pipe($this->fd, $remote); 
  69.  
  70.     $pool->submit($pipe1); 
  71.     $pool->submit($pipe2); 
  72.   } 
  73.  
  74. class ProxyWorker extends Worker 
  75.   public $pipePool
  76.   public function __construct($pipePool
  77.   { 
  78.     $this->pipePool = $pipePool
  79.   } 
  80.  
  81. $server = stream_socket_server('tcp://0.0.0.0:1080'$errno$errstr); 
  82. if ($server === false) 
  83.   exit($errstr); 
  84.  
  85. $pipePool = new Pool(200, Worker::class); 
  86. $pool = new Pool(50, 'ProxyWorker', [$pipePool]); 
  87.  
  88. for( ; ; ) { 
  89.   $fd = @stream_socket_accept($server, 60); 
  90.   if ($fd === false) 
  91.     continue
  92.   $pool->submit(new Client($fd)); 
  93. }

Tags: PHP代理服务器 socks5

分享到: