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


Posted in PHP onMay 05, 2016
100多行PHP代码实现socks5代理服务器,这次是使用swoole纯异步来写,使用状态机来处理数据。目前用它访问开源中国木有压力,但访问网易新闻就压力山大。我发现我用别的语言写得代理,访问网易新闻都压力大。嘎嘎,学艺不精。
对swoole理解不深,不知道怎么处理socket shutdown只关闭读/写这样,还有就是连接超时,读写超时这种怎么处理。在网上看到作者说要用定时器,感觉好麻烦,所以,这次的代理,虽然个人用,一般不会有什么问题,但离产品级的代理,还有段路要走。

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



<?php
class Client
{
 public $connected = true;
 public $data = '';
 public $remote = null;
 public $status = 0;
}
class Server
{
 public $clients = [];
 public function start()
 {
  $server = new swoole_server('0.0.0.0', 8388, SWOOLE_BASE, SWOOLE_SOCK_TCP);
  $server->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[1] !== 0x05) {
      echo '协议不正确:' . $request[1], PHP_EOL;
      $server->close($fd);
      break;
     }
     $nmethods = $request[2];
     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) < 5)
     break;
    $request = unpack('c*', $client->data);
    $aType = $request[4];
    if ($aType === 0x03) { // domain
     $domainLen = $request[5];
     if (strlen($client->data) < 5 + $domainLen + 2) { 
      break; 
     }
     $domain = substr($client->data, 5, $domainLen);
     $port = unpack('n', substr($client->data, 5 + $domainLen, 2))[1]; 
     $client->data = substr($client->data, 5 + $domainLen + 2);
    } else if ($aType === 0x01) { // ipv4
     $domain = long2ip(unpack('N', substr($client->data, 4, 4))[1]);
     $port = unpack('n', substr($client->data, 8, 2))[1]; 
     $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 < strlen($client->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();
PHP 相关文章推荐
用PHP程序实现支持页面后退的两种方法
Jun 30 PHP
php中把美国时间转为北京时间的自定义函数分享
Jul 28 PHP
PHP遍历XML文档所有节点的方法
Mar 12 PHP
WordPress开发中用于标题显示的相关函数使用解析
Jan 07 PHP
详解WordPress开发中用于获取分类及子页面的函数用法
Jan 08 PHP
PHP中的随机性 你觉得自己幸运吗?
Jan 22 PHP
Centos PHP 扩展Xchche的安装教程
Jul 09 PHP
利用switch语句进行多选一判断的实例代码
Nov 14 PHP
PHP基于DOMDocument解析和生成xml的方法分析
Jul 17 PHP
Thinkphp5行为使用方法汇总
Dec 21 PHP
PHP PDOStatement::nextRowset讲解
Feb 01 PHP
PHP Swoole异步读取、写入文件操作示例
Oct 24 PHP
PHP随机数 C扩展随机数
May 04 #PHP
PHP正则表达式过滤html标签属性(DEMO)
May 04 #PHP
Joomla使用Apache重写模式的方法
May 04 #PHP
Joomla开启SEF的方法
May 04 #PHP
Joomla简单判断用户是否登录的方法
May 04 #PHP
Joomla实现组件中弹出一个模式(modal)窗口的方法
May 04 #PHP
joomla组件开发入门教程
May 04 #PHP
You might like
php实现统计网站在线人数的方法
2015/05/12 PHP
确保Laravel网站不会被嵌入到其他站点中的方法
2019/10/18 PHP
JS 创建对象(常见的几种方法)
2008/11/03 Javascript
自己的js工具_Form 封装
2009/08/21 Javascript
微博@符号的用户名提示效果。(想@到谁?)
2010/11/05 Javascript
使用jquery读取html5 localstorage的值的方法
2013/01/04 Javascript
html文件中jquery与velocity变量中的$冲突的解决方法
2013/11/01 Javascript
Javascript中setTimeOut和setInterval的定时器用法
2015/06/12 Javascript
JavaScript中解析JSON数据的三种方法
2015/07/03 Javascript
浅析jquery与checkbox的checked属性的问题
2016/04/27 Javascript
Bootstrap精简教程中秋大放送
2016/09/15 Javascript
原生js验证简洁注册登录页面
2016/12/17 Javascript
jquery.tableSort.js表格排序插件使用方法详解
2020/08/12 Javascript
详解Vue.js入门环境搭建
2017/03/17 Javascript
js利用for in循环获取 一个对象的所有属性以及值的实例
2017/03/30 Javascript
ES6新特性之Object的变化分析
2017/03/31 Javascript
jQuery遮罩层实例讲解
2017/05/11 jQuery
vue 左滑删除功能的示例代码
2019/01/28 Javascript
nodejs和react实现即时通讯简易聊天室功能
2019/08/21 NodeJs
Python中的两个内置模块介绍
2015/04/05 Python
Python pickle模块用法实例分析
2015/05/27 Python
Python如何读取MySQL数据库表数据
2017/03/11 Python
初次部署django+gunicorn+nginx的方法步骤
2019/09/11 Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
2020/02/26 Python
Python爬取豆瓣数据实现过程解析
2020/10/27 Python
python中altair可视化库实例用法
2021/01/26 Python
HTML5实现WebSocket协议原理浅析
2014/07/07 HTML / CSS
Gtech官方网站:地毯清洁器、吸尘器及园艺设备
2018/05/23 全球购物
护理专科毕业生自荐书范文
2014/02/19 职场文书
小学一年级评语大全
2014/04/22 职场文书
《谁的本领大》教后反思
2014/04/25 职场文书
化工工艺设计求职信
2014/06/25 职场文书
2014年司法所工作总结
2014/11/22 职场文书
结婚司仪主持词
2015/06/29 职场文书
食品卫生管理制度
2015/08/06 职场文书
java如何实现获取客户端ip地址的示例代码
2022/04/07 Java/Android