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 相关文章推荐
多重?l件?合查?(二)
Oct 09 PHP
php源代码安装常见错误与解决办法分享
May 28 PHP
php缩小png图片不损失透明色的解决方法
Dec 25 PHP
Laravel中使用自己编写类库的3种方法
Feb 10 PHP
php使用curl出现Expect:100-continue解决方法
Mar 03 PHP
PHP的Yii框架使用中的一些错误解决方法与建议
Aug 21 PHP
php生成酷炫的四个字符验证码
Apr 22 PHP
PHP7扩展开发之hello word实现方法详解
Jan 15 PHP
基于php中echo用逗号和用点号的区别详解
Jan 23 PHP
PHP实现的数组和XML文件相互转换功能示例
Mar 15 PHP
PHP addAttribute()函数讲解
Feb 03 PHP
php常用字符串查找函数strstr()与strpos()实例分析
Jun 21 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开发工具之vs2005图解
2008/01/12 PHP
php调用C代码的实现方法
2014/03/11 PHP
Zend Framework教程之分发器Zend_Controller_Dispatcher用法详解
2016/03/07 PHP
php时间函数用法分析
2016/05/28 PHP
Laravel6.2中用于用户登录的新密码确认流程详解
2019/10/16 PHP
Laravel5.5 视图 - 创建视图和数据传递示例
2019/10/21 PHP
详细解密jsonp跨域请求
2015/04/15 Javascript
JavaScript实现动画打开半透明提示层的方法
2015/04/21 Javascript
javascript控制图片播放的实现代码
2020/07/29 Javascript
jQuery实现本地预览上传图片功能
2016/01/08 Javascript
浅析jQuery 3.0中的Data
2016/06/14 Javascript
NodeJS测试框架mocha入门教程
2017/03/28 NodeJs
JS实现汉字与Unicode码相互转换的方法详解
2017/04/28 Javascript
js中的 || 与 &amp;&amp; 运算符详解
2018/05/24 Javascript
浅析Vue 生命周期
2018/06/21 Javascript
Vue动态生成表格的行和列
2019/07/18 Javascript
解决layui调用自定义方法提示未定义的问题
2019/09/14 Javascript
vue子组件改变父组件传递的prop值通过sync实现数据双向绑定(DEMO)
2020/02/01 Javascript
[52:07]完美世界DOTA2联赛PWL S3 LBZS vs access 第二场 12.10
2020/12/13 DOTA
Python中使用Inotify监控文件实例
2015/02/14 Python
用Python写一个模拟qq聊天小程序的代码实例
2019/03/06 Python
Python下opencv图像阈值处理的使用笔记
2019/08/04 Python
pytorch索引查找 index_select的例子
2019/08/18 Python
python 画3维轨迹图并进行比较的实例
2019/12/06 Python
python第三方库学习笔记
2020/02/07 Python
python实现Pyecharts实现动态地图(Map、Geo)
2020/03/25 Python
最新的大学生找工作自我评价
2013/09/29 职场文书
环境工程大学生个人的自我评价
2013/10/08 职场文书
认识深刻的检讨书
2014/02/16 职场文书
2014年幼儿园教学工作总结
2014/12/04 职场文书
工程项目经理岗位职责
2015/02/02 职场文书
2015年十一国庆节演讲稿
2015/03/20 职场文书
劳动仲裁代理词范文
2015/05/25 职场文书
大学生志愿者心得体会
2016/01/15 职场文书
Redis6.0搭建集群Redis-cluster的方法
2021/05/08 Redis
JavaScript严格模式不支持八进制的问题讲解
2021/11/07 Javascript