xgnic 发表于 2018-2-11 12:12:09


            100多行PHP代码实现socks5代理服务器,这次是使用swoole纯异步来写,使用状态机来处理数据。目前用它访问开源中国木有压力,但访问网易新闻就压力山大。我发现我用别的语言写得代理,访问网易新闻都压力大。嘎嘎,学艺不精。
对swoole理解不深,不知道怎么处理socket shutdown只关闭读/写这样,还有就是连接超时,读写超时这种怎么处理。在网上看到作者说要用定时器,感觉好麻烦,所以,这次的代理,虽然个人用,一般不会有什么问题,但离产品级的代理,还有段路要走。
如果要利用多核,就使用process模式,设置worker个数为cpu数量即可。
set([
   'max_conn' => 1000,
   'daemonize' => 1,
   'reactor_num' => 1,
   'worker_num' => 1,
   'dispatch_mode' => 2,
   'buffer_output_size' => 128 * 1024 * 1024,
   'open_cpu_affinity' => 1,
   'open_tcp_nodelay' => 1,
   'log_file' => 'socks5_server.log',
]);
$server->on('connect', [$this, 'onConnect']);
$server->on('receive', [$this, 'onReceive']);
$server->on('close', [$this, 'onClose']);
$server->start();
}
public function onConnect($server, $fd, $fromID)
{
$this->clients[$fd] = new Client();
}
public function onReceive($server, $fd, $fromID, $data)
{
($this->clients[$fd])->data .= $data;
$this->parse($server, $fd);
}
public function onClose($server, $fd, $fromID)
{
$client = $this->clients[$fd];
$client->connected = false;
}
private function parse($server, $fd)
{
$client = $this->clients[$fd];
switch ($client->status) {
   case 0: {
    if (strlen($client->data) >= 2) {
   $request = unpack('c*', substr($client->data, 0, 2));
   if ($request !== 0x05) {
      echo '协议不正确:' . $request, PHP_EOL;
      $server->close($fd);
      break;
   }
   $nmethods = $request;
   if (strlen($client->data) >= 2 + $nmethods) {
      $client->data = substr($client->data, 2 + $nmethods);
      $server->send($fd, "\x05\x00");
      $client->status = 1;
   }
    }
   }
   case 1: {
    if (strlen($client->data) data);
    $aType = $request;
    if ($aType === 0x03) { // domain
   $domainLen = $request;
   if (strlen($client->data) data, 5, $domainLen);
   $port = unpack('n', substr($client->data, 5 + $domainLen, 2));
   $client->data = substr($client->data, 5 + $domainLen + 2);
    } else if ($aType === 0x01) { // ipv4
   $domain = long2ip(unpack('N', substr($client->data, 4, 4)));
   $port = unpack('n', substr($client->data, 8, 2));
   $client->data = substr($client->data, 10);
    } else {
   echo '不支持的atype:' . $aType, PHP_EOL;
   $server->close($fd);
   break;
    }
    $remote = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
    $remote->on('connect', function($cli) use($client, $server, $fd, $remote) {
   $server->send($fd, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00");
   $client->status = 2;
   $client->remote = $remote;
    });
    $remote->on("error", function(swoole_client $cli) use($server, $fd) {
   //$server->send($fd, ""); // todo 连接不上remote
   echo 'connect to remote error.', PHP_EOL;
   $server->close($fd);
    });
    $remote->on('receive', function($cli, $data) use($server, $fd, $client) {
   if (!$client->connected) {
      echo 'connection has been closed.', PHP_EOL;
      return;
   }
   $server->send($fd, $data);
    });
    $remote->on('close', function($cli) use($server, $fd, $client) {
   $client->remote = null;
    });
    if ($aType === 0x03) {
   swoole_async_dns_lookup($domain, function($host, $ip) use($remote, $port, $server, $fd) {
      //todo 当host为空时的处理。貌似不存在的域名都解析成了本机的外网ip,奇怪
      if (empty($ip) || empty($host)) {
       echo "host:{$host}, ip:{$ip}\n";
       $server->close($fd);
       return;
      }
      $remote->connect($ip, $port);
   });
    } else {
   $remote->connect($domain, $port);
    }
   }
   case 2: {
    if (strlen($client->data) === 0) {
   break;
    }
    if ($client->remote === null) {
   echo 'remote connection has been closed.', PHP_EOL;
   break;
    }
    $sendByteCount = $client->remote->send($client->data);
    if ($sendByteCount === false || $sendByteCount data)) {
   echo 'data length:' , strlen($client->data), ' send byte count:', $sendByteCount, PHP_EOL;
   echo $client->data, PHP_EOL;
   $server->close($fd);
    }
    $client->data = '';
   }
}
}
}
(new Server())->start();
页: [1]
查看完整版本: 100多行PHP代码实现socks5代理服务器[2]