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

100多行PHP代码实现socks5代理服务器[2]

发布:smiling 来源: PHP粉丝网  添加日期:2021-08-01 14:24:42 浏览: 评论:0 

100多行PHP代码实现socks5代理服务器,这次是使用swoole纯异步来写,使用状态机来处理数据。目前用它访问开源中国木有压力,但访问网易新闻就压力山大,我发现我用别的语言写得代理,访问网易新闻都压力大,嘎嘎,学艺不精。

对swoole理解不深,不知道怎么处理socket shutdown只关闭读/写这样,还有就是连接超时,读写超时这种怎么处理。在网上看到作者说要用定时器,感觉好麻烦,所以,这次的代理,虽然个人用,一般不会有什么问题,但离产品级的代理,还有段路要走。

如果要利用多核,就使用process模式,设置worker个数为cpu数量即可。

  1. <?php 
  2. class Client 
  3. { 
  4.  public $connected = true; 
  5.  public $data = ''; 
  6.  public $remote = null; 
  7.  public $status = 0; 
  8. } 
  9. class Server 
  10. { 
  11.  public $clients = []; 
  12.  public function start() 
  13.  { 
  14.   $server = new swoole_server('0.0.0.0', 8388, SWOOLE_BASE, SWOOLE_SOCK_TCP); 
  15.   $server->set([ 
  16.    'max_conn' => 1000,  
  17.    'daemonize' => 1, 
  18.    'reactor_num' => 1, 
  19.    'worker_num' => 1, 
  20.    'dispatch_mode' => 2, 
  21.    'buffer_output_size' => 128 * 1024 * 1024, 
  22.    'open_cpu_affinity' => 1, 
  23.    'open_tcp_nodelay' => 1, 
  24.    'log_file' => 'socks5_server.log', 
  25.   ]); 
  26.   $server->on('connect', [$this, 'onConnect']); 
  27.   $server->on('receive', [$this, 'onReceive']); 
  28.   $server->on('close', [$this, 'onClose']); 
  29.   $server->start(); 
  30.  } 
  31.  public function onConnect($server, $fd, $fromID) 
  32.  { 
  33.   $this->clients[$fd] = new Client(); 
  34.  } 
  35.  public function onReceive($server, $fd, $fromID, $data) 
  36.  { 
  37.   ($this->clients[$fd])->data .= $data; 
  38.   $this->parse($server, $fd);  
  39.  } 
  40.  public function onClose($server, $fd, $fromID) 
  41.  { 
  42.   $client = $this->clients[$fd]; 
  43.   $client->connected = false; 
  44.  } 
  45.  private function parse($server, $fd)  
  46.  { 
  47.   $client = $this->clients[$fd]; 
  48.  
  49.   switch ($client->status) { 
  50.    case 0: { 
  51.     if (strlen($client->data) >= 2) { 
  52.      $request = unpack('c*', substr($client->data, 0, 2)); 
  53.      if ($request[1] !== 0x05) { 
  54.       echo '协议不正确:' . $request[1], PHP_EOL; 
  55.       $server->close($fd); 
  56.       break; 
  57.      } 
  58.      $nmethods = $request[2]; 
  59.      if (strlen($client->data) >= 2 + $nmethods) { 
  60.       $client->data = substr($client->data, 2 + $nmethods); 
  61.       $server->send($fd, "\x05\x00"); 
  62.       $client->status = 1; 
  63.      } 
  64.     } 
  65.    } 
  66.    case 1: { 
  67.     if (strlen($client->data) < 5) 
  68.      break; 
  69.     $request = unpack('c*', $client->data); 
  70.     $aType = $request[4]; 
  71.     if ($aType === 0x03) { // domain 
  72.      $domainLen = $request[5]; 
  73.      if (strlen($client->data) < 5 + $domainLen + 2) {  
  74.       break;  
  75.      } 
  76.      $domain = substr($client->data, 5, $domainLen); 
  77.      $port = unpack('n', substr($client->data, 5 + $domainLen, 2))[1];  
  78.      $client->data = substr($client->data, 5 + $domainLen + 2); 
  79.     } else if ($aType === 0x01) { // ipv4 
  80.      $domain = long2ip(unpack('N', substr($client->data, 4, 4))[1]); 
  81.      $port = unpack('n', substr($client->data, 8, 2))[1];  
  82.      $client->data = substr($client->data, 10); 
  83.     } else { 
  84.      echo '不支持的atype:' . $aType, PHP_EOL; 
  85.      $server->close($fd); 
  86.      break; 
  87.     } 
  88.  
  89.     $remote = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); 
  90.     $remote->on('connect', function($cli) use($client, $server, $fd, $remote) { 
  91.      $server->send($fd, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00"); 
  92.      $client->status = 2; 
  93.      $client->remote = $remote; 
  94.     }); 
  95.     $remote->on("error", function(swoole_client $cli) use($server, $fd) { 
  96.      //$server->send($fd, ""); // todo 连接不上remote 
  97.      echo 'connect to remote error.', PHP_EOL; 
  98.      $server->close($fd); 
  99.     }); 
  100.     $remote->on('receive', function($cli, $data) use($server, $fd, $client) { 
  101.      if (!$client->connected) { 
  102.       echo 'connection has been closed.', PHP_EOL; 
  103.       return; 
  104.      } 
  105.      $server->send($fd, $data); 
  106.     }); 
  107.     $remote->on('close', function($cli) use($server, $fd, $client) { 
  108.      $client->remote = null; 
  109.     }); 
  110.     if ($aType === 0x03) { 
  111.      swoole_async_dns_lookup($domain, function($host, $ip) use($remote, $port, $server, $fd) { 
  112.       //todo 当host为空时的处理。貌似不存在的域名都解析成了本机的外网ip,奇怪 
  113.       if (emptyempty($ip) || emptyempty($host)) { 
  114.        echo "host:{$host}, ip:{$ip}\n"; 
  115.        $server->close($fd); 
  116.        return; 
  117.       } 
  118.       $remote->connect($ip, $port); 
  119.      }); 
  120.     } else { 
  121.      $remote->connect($domain, $port); 
  122.     } 
  123.    } 
  124.    case 2: { 
  125.     if (strlen($client->data) === 0) { 
  126.      break; 
  127.     } 
  128.     if ($client->remote === null) { 
  129.      echo 'remote connection has been closed.', PHP_EOL; 
  130.      break; 
  131.     } 
  132.  
  133.     $sendByteCount = $client->remote->send($client->data); 
  134.     if ($sendByteCount === false || $sendByteCount < strlen($client->data)) { 
  135.      echo 'data length:' , strlen($client->data), ' send byte count:', $sendByteCount, PHP_EOL;  
  136.      echo $client->data, PHP_EOL; 
  137.      $server->close($fd);  
  138.     } 
  139.     $client->data = ''; 
  140.    } 
  141.   } 
  142.  } 
  143. } 
  144.  
  145. (new Server())->start();

Tags: socks5代理服务器

分享到: