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的引用计数机制
Jun 14 PHP
PHP中HTML标签过滤技巧
Jan 07 PHP
codeigniter数据库操作函数汇总
Jun 12 PHP
PHP使用array_multisort对多个数组或多维数组进行排序
Dec 16 PHP
浅谈php自定义错误日志
Feb 13 PHP
深入探究PHP的多进程编程方法
Aug 18 PHP
ThinkPHP数据操作方法总结
Sep 28 PHP
基于PHP代码实现中奖概率算法可用于刮刮卡、大转盘等抽奖算法
Dec 20 PHP
php框架CodeIgniter主从数据库配置方法分析
May 25 PHP
php实现分页功能的详细实例方法
Sep 29 PHP
php桥接模式应用案例分析
Oct 23 PHP
PHP连接SQL server数据库测试脚本运行实例
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基础学习笔记
2007/03/18 PHP
php读取数据库信息的几种方法
2008/05/24 PHP
PHP字符串的编码问题的详细介绍
2013/04/27 PHP
PHP的基本常识小结
2013/07/05 PHP
Laravel 5框架学习之Laravel入门和新建项目
2015/04/07 PHP
php中时间函数date及常用的时间计算
2017/05/12 PHP
PHP后台备份MySQL数据库的源码实例
2019/03/18 PHP
js 中 document.createEvent的用法
2010/08/29 Javascript
读jQuery之二(两种扩展)
2011/06/11 Javascript
鼠标移到导航当前位置的LI变色处于选中状态
2013/08/23 Javascript
手机号码,密码正则验证
2014/09/04 Javascript
js实现div弹出层的方法
2014/11/20 Javascript
JS实现横向与竖向两个选项卡Tab联动的方法
2015/09/27 Javascript
学习JavaScript设计模式之代理模式
2016/01/12 Javascript
jQuery 更改checkbox的状态,无效的解决方法
2016/07/22 Javascript
微信小程序 框架详解及实例应用
2016/09/26 Javascript
jQuery编写设置和获取颜色的插件
2017/01/09 Javascript
解析jquery easyui tree异步加载子节点问题
2017/03/08 Javascript
Bootstrap Table使用整理(二)
2017/06/09 Javascript
在Vue中使用echarts的实例代码(3种图)
2017/07/10 Javascript
vue.js使用v-model指令实现的数据双向绑定功能示例
2018/05/22 Javascript
javascript将扁平的数据转为树形结构的高效率算法
2020/02/27 Javascript
在vue项目中利用popstate处理页面返回的操作介绍
2020/08/06 Javascript
[02:32]DOTA2英雄基础教程 祸乱之源
2013/12/23 DOTA
Python注释详解
2016/06/01 Python
Python爬虫实现(伪)球迷速成
2018/06/10 Python
python读取excel指定列数据并写入到新的excel方法
2018/07/10 Python
基于python实现名片管理系统
2018/11/30 Python
Python列表list操作相关知识小结
2020/01/29 Python
中国高端鲜花第一品牌:roseonly(一生只送一人)
2017/02/12 全球购物
js实现弹框效果
2021/03/24 Javascript
机电专业体育教师求职信
2013/09/21 职场文书
幼儿园大班新学期寄语
2014/01/18 职场文书
中国在我心中演讲稿
2014/09/13 职场文书
2015年客服工作总结范文
2015/04/02 职场文书
Windows环境下实现批量执行Sql文件
2021/10/05 SQL Server