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中数据的批量导入(csv文件)
Oct 09 PHP
WML,Apache,和 PHP 的介绍
Oct 09 PHP
PHP strtr() 函数使用说明
Nov 21 PHP
PHP 图片上传代码
Sep 13 PHP
php 下载保存文件保存到本地的两种实现方法
Aug 12 PHP
php目录操作实例代码
Feb 21 PHP
PHP实现利用MySQL保存session的方法
Aug 23 PHP
浅谈php优化需要注意的地方
Nov 27 PHP
详解PHP的Yii框架中日志的相关配置及使用
Dec 08 PHP
thinkphp自定义权限管理之名称判断方法
Apr 01 PHP
Laravel中unique和exists验证规则的优化详解
Jan 28 PHP
PHP执行普通shell命令流程解析
Aug 24 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实现mysql封装类示例
2014/05/07 PHP
PHP CURL 内存泄露问题解决方法
2015/02/12 PHP
php短网址和数字之间相互转换的方法
2015/03/13 PHP
PHP解析url并得到url参数方法总结
2018/10/11 PHP
使用Java实现简单的server/client回显功能的方法介绍
2013/05/03 Javascript
Json解析的方法小结
2016/06/22 Javascript
jQuery简单实现iframe的高度根据页面内容自适应的方法
2016/08/01 Javascript
JavaScript SHA512加密算法详细代码
2016/10/06 Javascript
jQuery插件echarts实现的去掉X轴、Y轴和网格线效果示例【附demo源码下载】
2017/03/04 Javascript
详解windows下vue-cli及webpack 构建网站(二)导入bootstrap样式
2017/06/17 Javascript
vue(2.x,3.0)配置跨域代理
2019/11/27 Javascript
JavaScript实现打砖块游戏
2020/02/25 Javascript
JavaScript实现电灯开关小案例
2020/03/30 Javascript
用Angular实现一个扫雷的游戏示例
2020/05/15 Javascript
vue实现从外部修改组件内部的变量的值
2020/07/30 Javascript
Python中的类学习笔记
2014/09/23 Python
Python设计模式中单例模式的实现及在Tornado中的应用
2016/03/02 Python
Python结巴中文分词工具使用过程中遇到的问题及解决方法
2017/04/15 Python
Python编程之变量赋值操作实例分析
2017/07/24 Python
python实现12306抢票及自动邮件发送提醒付款功能
2018/03/08 Python
dataframe设置两个条件取值的实例
2018/04/12 Python
python找出列表中大于某个阈值的数据段示例
2019/11/24 Python
python颜色随机生成器的实例代码
2020/01/10 Python
python实现逢七拍腿小游戏的思路详解
2020/05/26 Python
Python环境使用OpenCV检测人脸实现教程
2020/10/19 Python
详解Pycharm第三方库的安装及使用方法
2020/12/29 Python
HTML5的新特性(1)
2016/03/03 HTML / CSS
印尼太阳百货公司网站:Matahari
2018/02/04 全球购物
英国屋顶用品和材料超市:Roofing Supplies UK
2019/08/24 全球购物
什么叫应用程序域?什么是受管制的代码?什么是强类型系统?什么是装箱和拆箱?
2016/08/13 面试题
Java语言程序设计测试题改错题部分
2014/07/22 面试题
上班玩游戏检讨书
2014/02/07 职场文书
教代会开幕词
2015/01/28 职场文书
爱心募捐通知范文
2015/04/27 职场文书
掌握一个领域知识,高效学习必备方法
2019/08/08 职场文书
MYSQL如何查看操作日志详解
2022/05/30 MySQL