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 XML操作的各种方法解析(比较详细)
Jun 17 PHP
php一个找二层目录的小东东
Aug 02 PHP
PhpDocumentor 2安装以及生成API文档的方法
May 21 PHP
php广告加载类用法实例
Sep 23 PHP
PHP使用mkdir创建多级目录的方法
Dec 22 PHP
php判断是否连接上网络的方法实例详解
Dec 14 PHP
PHP中让json_encode不自动转义斜杠“/”的方法
Feb 28 PHP
PHP单例模式简单用法示例
Jun 23 PHP
PHP实现的折半查找算法示例
Dec 19 PHP
PHP长连接实现与使用方法详解
Feb 11 PHP
php防止表单重复提交实例讲解
Feb 11 PHP
ThinkPHP框架实现FTP图片上传功能示例
Apr 08 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
Apache2 httpd.conf 中文版
2006/12/06 PHP
笑谈配置,使用Smarty技术
2007/01/04 PHP
PHP写入WRITE编码为UTF8的文件的实现代码
2008/07/07 PHP
PHP解密Unicode及Escape加密字符串
2015/05/17 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
解决PHP上传非标准格式的图片pjpeg失败的方法
2017/03/12 PHP
php判断/计算闰年的方法小结【三种方法】
2019/07/06 PHP
精解window.setTimeout()&amp;window.setInterval()使用方式与参数传递问题!
2007/11/23 Javascript
Jquery 1.42 checkbox 全选和反选代码
2010/03/27 Javascript
两种js监听滚轮事件的实现方法
2016/05/13 Javascript
利用Vue.js指令实现全选功能
2016/09/08 Javascript
Bootstrap禁用响应式布局的实现方法
2017/03/09 Javascript
jQuery表单设置值的方法
2017/06/30 jQuery
jQuery扩展_动力节点Java学院整理
2017/07/05 jQuery
Vee-Validate的使用方法详解
2017/09/22 Javascript
vue-cli脚手架config目录下index.js配置文件的方法
2018/03/13 Javascript
关于vue的npm run dev和npm run build的区别介绍
2019/01/14 Javascript
JavaScript使用小插件实现倒计时的方法讲解
2019/03/11 Javascript
从0到1搭建Element的后台框架的方法步骤
2019/04/10 Javascript
[54:51]Ti4 冒泡赛第二轮LGD vs C9 3
2014/07/14 DOTA
[37:50]VP vs TNC Supermajor小组赛B组 BO3 第一场 6.2
2018/06/03 DOTA
Python中type的构造函数参数含义说明
2015/06/21 Python
Python正则获取、过滤或者替换HTML标签的方法
2016/01/28 Python
Python选课系统开发程序
2016/09/02 Python
ansible作为python模块库使用的方法实例
2017/01/17 Python
Python通过paramiko远程下载Linux服务器上的文件实例
2018/12/27 Python
在Python中os.fork()产生子进程的例子
2019/08/08 Python
Python3.7在anaconda里面使用IDLE编译器的步骤详解
2020/04/29 Python
HTML5逐步分析实现拖放功能的方法
2020/09/30 HTML / CSS
New Balance波兰官方商城:始于1906年,百年慢跑品牌
2017/08/15 全球购物
京东全球售:直邮香港,澳门,台湾,美国,澳大利亚等地区
2017/09/24 全球购物
FLOS美国官网:意大利高级照明工艺的传奇
2018/08/07 全球购物
新年爱情寄语
2014/04/08 职场文书
竞选文艺委员演讲稿
2014/04/28 职场文书
2014年反洗钱工作总结
2014/11/22 职场文书
圆明园观后感
2015/06/03 职场文书