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 相关文章推荐
桌面中心(一)创建数据库
Oct 09 PHP
PHP简介
Oct 09 PHP
php中文字母数字验证码实现代码
Apr 25 PHP
FleaPHP的安全设置方法
Sep 15 PHP
浅析PHP中Collection 类的设计
Jun 21 PHP
wordpress自定义url参数实现路由功能的代码示例
Nov 28 PHP
30个php操作redis常用方法代码例子
Jul 05 PHP
ThinkPHP之用户注册登录留言完整实例
Jul 22 PHP
浅谈php的优缺点
Jul 14 PHP
PHP创建自己的Composer包方法
Apr 09 PHP
php empty 函数判断结果为空但实际值却为非空的原因解析
May 28 PHP
PHP从零开始打造自己的MVC框架之路由类实现方法分析
Jun 03 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
利用谷歌 Translate API制作自己的翻译脚本
2014/06/04 PHP
yii框架结合charjs实现统计30天数据的方法
2020/04/04 PHP
JavaScript中继承的一些示例方法与属性参考
2010/08/07 Javascript
获取offsetTop和offsetLeft值的js代码(兼容)
2013/04/16 Javascript
jQuery插件PageSlide实现左右侧栏导航菜单
2015/04/12 Javascript
简述jQuery ajax的执行顺序
2016/01/05 Javascript
JavaScript的React框架中的JSX语法学习入门教程
2016/03/05 Javascript
jquery实现一个简单的表单验证实例
2016/03/30 Javascript
在javascript中使用com组件的简单实现方法
2016/08/17 Javascript
Vue.js仿Metronic高级表格(二)数据渲染
2017/04/19 Javascript
关于JavaScript语句后面的分号问题
2017/12/07 Javascript
优雅的在React项目中使用Redux的方法
2018/11/10 Javascript
jQuery使用$.extend(true,object1, object2);实现深拷贝对象的方法分析
2019/03/06 jQuery
详解Angular Karma测试的持续集成实践
2019/11/15 Javascript
VUE使用 wx-open-launch-app 组件开发微信打开APP功能
2020/08/11 Javascript
[01:25:09]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第二场
2014/05/24 DOTA
[57:55]完美世界DOTA2联赛PWL S3 Magma vs Phoenix 第二场 12.12
2020/12/16 DOTA
栈和队列数据结构的基本概念及其相关的Python实现
2015/08/24 Python
一步步教你用python的scrapy编写一个爬虫
2019/04/17 Python
python解析yaml文件过程详解
2019/08/30 Python
详解pandas中iloc, loc和ix的区别和联系
2020/03/09 Python
Python实现汇率转换操作
2020/05/03 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
2020/09/03 Python
HTML5+CSS3网页加载进度条的实现,下载进度条的代码实例
2016/12/30 HTML / CSS
施华洛世奇水晶荷兰官方网站:SWAROVSKI荷兰
2017/05/12 全球购物
大学生四个方面的自我评价
2013/09/19 职场文书
专业实习自我鉴定
2013/10/29 职场文书
实习心得体会
2014/01/02 职场文书
服务承诺书范文
2014/05/19 职场文书
中学教师暑期培训方案
2014/08/27 职场文书
办公室个人总结
2015/02/28 职场文书
拔河比赛新闻稿
2015/07/17 职场文书
浅谈redis五大数据结构和使用场景
2021/04/12 Redis
浅谈哪个Python库才最适合做数据可视化
2021/06/28 Python
微软团队与 NASA 科学家和惠普企业(HPE)的工程师合作
2022/04/21 数码科技
Win11 vmware不兼容怎么办?Win11与VMware虚拟机不兼容的解决方法
2023/01/09 数码科技