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编写一个简单的路由类
Apr 13 PHP
php数组函数序列之array_unique() - 去除数组中重复的元素值
Oct 29 PHP
smarty巧妙处理iframe中内容页的代码
Mar 07 PHP
CodeIgniter框架URL路由总结
Sep 03 PHP
配置php.ini实现PHP文件上传功能
Nov 27 PHP
PHP输入输出流学习笔记
May 12 PHP
PHP根据session与cookie用户登录状态操作类的代码
May 13 PHP
php观察者模式应用场景实例详解
Feb 03 PHP
php实现在线考试系统【附源码】
Sep 18 PHP
php生成微信红包数组的方法
Sep 05 PHP
基于Laravel-admin 后台的自定义页面用法详解
Sep 30 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
Apr 26 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添加Xdebug扩展的方法
2014/02/12 PHP
PHP CURL或file_get_contents获取网页标题的代码及两者效率的稳定性问题
2015/11/30 PHP
PHP实现的Redis多库选择功能单例类
2017/07/27 PHP
laravel 5异常错误:FatalErrorException in Handler.php line 38的解决
2017/10/12 PHP
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
删除select中所有option选项jquery代码
2013/08/12 Javascript
兼容IE、firefox以及chrome的js获取时间(getFullYear)
2014/07/04 Javascript
JS运动框架之分享侧边栏动画实例
2015/03/03 Javascript
深入理解JavaScript系列(22):S.O.L.I.D五大原则之依赖倒置原则DIP详解
2015/03/05 Javascript
JavaScript获取网页中第一个链接ID的方法
2015/04/03 Javascript
jQuery移动web开发中的页面初始化与加载事件
2015/12/03 Javascript
深究AngularJS——ng-checked(回写:带真实案例代码)
2017/06/13 Javascript
通过button将form表单的数据提交到action层的实例
2017/09/08 Javascript
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
vue项目关闭eslint校验
2018/03/21 Javascript
JS调用安卓手机摄像头扫描二维码
2018/10/16 Javascript
基于vue-cli搭建多模块且各模块独立打包的项目
2019/06/12 Javascript
vue+elementUI 复杂表单的验证、数据提交方案问题
2019/06/24 Javascript
jQuery鼠标滑过横向时间轴样式(代码详解)
2019/11/01 jQuery
Windows上node.js的多版本管理工具用法实例分析
2019/11/06 Javascript
python翻译软件实现代码(使用google api完成)
2013/11/26 Python
python脚本实现xls(xlsx)转成csv
2016/04/10 Python
python使用matplotlib绘制柱状图教程
2017/02/08 Python
fastcgi文件读取漏洞之python扫描脚本
2017/04/23 Python
对python制作自己的数据集实例讲解
2018/12/12 Python
python Matplotlib底图中鼠标滑过显示隐藏内容的实例代码
2019/07/31 Python
Python实例方法、类方法、静态方法区别详解
2020/09/05 Python
python 实现有道翻译功能
2021/02/26 Python
详解CSS3的box-shadow属性制作边框阴影效果的方法
2016/05/10 HTML / CSS
美国按摩椅批发网站:Titan Chair
2018/12/27 全球购物
美国和加拿大计算机和电子产品购物网站:TigerDirect.com
2019/09/13 全球购物
俄罗斯连接商品和买家的在线平台:goods.ru
2020/11/30 全球购物
2014年办公室人员工作总结
2014/12/09 职场文书
优秀班主任推荐材料
2014/12/17 职场文书
排查MySQL生产环境索引没有效果
2022/04/11 MySQL
前端canvas中物体边框和控制点的实现示例
2022/08/05 Javascript