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 相关文章推荐
WHOIS类的修改版
Oct 09 PHP
php 各种应用乱码问题的解决方法
May 09 PHP
PHP IF ELSE简化/三元一次式的使用
Aug 22 PHP
PHPThumb PHP 图片缩略图库
Mar 11 PHP
php定时删除文件夹下文件(清理缓存文件)
Jan 23 PHP
PHP保留两位小数并且四舍五入及不四舍五入的方法
Sep 22 PHP
PHP实现的简单缓存类
Jul 29 PHP
php生成验证码函数
Oct 20 PHP
php中时间函数date及常用的时间计算
May 12 PHP
Mac系统完美安装PHP7详细教程
Jun 06 PHP
PHP基于cookie实现统计在线人数功能示例
Jan 16 PHP
PHP PDOStatement::closeCursor讲解
Jan 30 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 session
2013/10/28 PHP
PHP实现变色验证码实例
2014/01/06 PHP
php使用include 和require引入文件的区别
2017/02/16 PHP
PHP实现将标点符号正则替换为空格的方法
2017/08/09 PHP
php解决安全问题的方法实例
2019/09/19 PHP
JavaScript 替换Html标签实现代码
2009/10/14 Javascript
JavaScript格式化数字的函数代码
2010/11/30 Javascript
jquery绑定事件不生效的解决方法
2014/02/11 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
Jquery日期选择datepicker插件用法实例分析
2015/06/08 Javascript
xmlplus组件设计系列之文本框(TextBox)(3)
2017/05/03 Javascript
Angular5给组件本身的标签添加样式class的方法
2018/04/07 Javascript
jQuery pjax 应用简单示例
2018/09/20 jQuery
Vue 递归多级菜单的实例代码
2019/05/05 Javascript
使用 vue 实现灭霸打响指英雄消失的效果附demo
2019/05/06 Javascript
使用watch在微信小程序中实现全局状态共享
2019/06/03 Javascript
[45:18]完美世界DOTA2联赛循环赛 PXG vs IO 第二场 11.06
2020/11/09 DOTA
Python创建xml的方法
2015/03/10 Python
Python实现一个简单的验证码程序
2017/11/03 Python
Python socket模块实现的udp通信功能示例
2019/04/10 Python
解决pycharm运行程序出现卡住scanning files to index索引的问题
2019/06/27 Python
Python学习之os模块及用法
2020/06/03 Python
python如何更新包
2020/06/11 Python
python中的split、rsplit、splitlines用法说明
2020/10/23 Python
CSS3 @media的基本用法总结
2019/09/10 HTML / CSS
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
泰坦健身器材:Titan Fitness
2018/02/13 全球购物
科茨沃尔德家居商店:Scotts of Stow
2018/06/29 全球购物
Stutterheim瑞典:瑞典高级外套时装品牌
2019/06/24 全球购物
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
生物专业个人自荐信范文
2013/11/29 职场文书
二年级小学生评语
2014/04/21 职场文书
会计求职自荐信
2014/06/20 职场文书
2014年重阳节活动策划方案书
2014/09/16 职场文书
告别网页搜索!教你用python实现一款属于自己的翻译词典软件
2021/06/03 Python
WIN10使用IIS部署ftp服务器详细教程
2022/08/05 Servers