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初学入门
Nov 19 PHP
PHP define函数的使用说明
Aug 27 PHP
对squid中refresh_pattern的一些理解和建议
Apr 17 PHP
php 保留小数点
Apr 21 PHP
php设计模式 Prototype (原型模式)代码
Jun 26 PHP
php中通过正则表达式下载内容中的远程图片的函数代码
Jan 10 PHP
php打造智能化的柱状图程序,用于报表等
Jun 19 PHP
php微信公众平台示例代码分析(二)
Dec 06 PHP
删除PHP数组中的重复元素的实现代码
Apr 10 PHP
PHP中Cookie的使用详解(简单易懂)
Apr 28 PHP
laravel通过创建自定义artisan make命令来新建类文件详解
Aug 17 PHP
Laravel 修改验证异常的响应格式实例代码详解
May 25 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
mysql 搜索之简单应用
2007/04/27 PHP
php unset全局变量运用问题的深入解析
2013/06/17 PHP
PHP中读取文件的几个方法总结(推荐)
2016/06/03 PHP
php生成静态页面并实现预览功能
2019/06/27 PHP
安装PHP扩展时解压官方 tgz 文件后没有configure文件无法进行配置编译的问题
2020/08/26 PHP
Javascript中eval函数的使用方法与示例
2007/04/09 Javascript
JavaScript将Table导出到Excel实现思路及代码
2013/03/13 Javascript
浅谈javascript 迭代方法
2015/01/21 Javascript
基于JavaScript实现快速转换文本语言(繁体中文和简体中文)
2016/03/07 Javascript
JavaScript数组合并的多种方法
2016/05/22 Javascript
基于Turn.js 实现翻书效果实例解析
2016/06/20 Javascript
微信小程序图表插件(wx-charts)实例代码
2017/01/17 Javascript
vue-router2.0 组件之间传参及获取动态参数的方法
2017/11/10 Javascript
Vue 项目部署到服务器的问题解决方法
2017/12/05 Javascript
详解webpack 热更新优化
2018/09/13 Javascript
通过cordova将vue项目打包为webapp的方法
2019/02/02 Javascript
vue中$refs, $emit, $on, $once, $off的使用详解
2019/05/26 Javascript
jquery绑定事件 bind和on的用法与区别分析
2020/05/22 jQuery
Python2.6版本中实现字典推导 PEP 274(Dict Comprehensions)
2015/04/28 Python
Python实现处理管道的方法
2015/06/04 Python
使用Python读取安卓手机的屏幕分辨率方法
2018/03/31 Python
Pycharm更换python解释器的方法
2018/10/29 Python
python爬虫实现爬取同一个网站的多页数据的实例讲解
2021/01/18 Python
HTML5新增元素如何兼容旧浏览器有哪些方法
2014/05/09 HTML / CSS
Stefania Mode美国:奢华设计师和时尚服装
2018/01/07 全球购物
Baby Tulai澳大利亚:美国婴儿背带品牌
2018/10/15 全球购物
美国社交购物市场:MassGenie
2019/02/18 全球购物
俄罗斯三星品牌商店:GalaxyStore
2020/11/04 全球购物
司机岗位职责
2013/11/15 职场文书
大学同学聚会邀请函
2014/01/19 职场文书
党校毕业个人总结
2015/02/28 职场文书
高质量“欢迎词”
2019/04/03 职场文书
七年级写作指导之游记作文
2019/10/07 职场文书
redis缓存存储Session原理机制
2021/11/20 Redis
68行Python代码实现带难度升级的贪吃蛇
2022/01/18 Python
JS中forEach()、map()、every()、some()和filter()的用法
2022/05/11 Javascript