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 生成文字png图片的代码
Apr 17 PHP
php读取文件内容至字符串中,同时去除换行、空行、行首行尾空格(Zjmainstay原创)
Jul 31 PHP
ThinkPHP无限级分类原理实现留言与回复功能实例
Oct 31 PHP
四种php中webservice实现的简单架构方法及实例
Feb 03 PHP
PHP获取数组的键与值方法小结
Jun 13 PHP
php类的扩展和继承用法实例
Jun 20 PHP
php实现xml与json之间的相互转换功能实例
Jul 07 PHP
PHP基于IMAP收取邮件的方法示例
Aug 07 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
Feb 10 PHP
PHPStudy下如何为Apache安装SSL证书的方法步骤
Jan 23 PHP
PHP利用DWZ.CN服务生成短网址
Aug 11 PHP
PHP+MySQL实现在线测试答题实例
Jan 02 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 字符串替换的方法
2012/01/10 PHP
逆序二维数组插入一元素的php代码
2012/06/08 PHP
基于在生产环境中使用php性能测试工具xhprof的详解
2013/06/03 PHP
PHP获取昨天、今天及明天日期的方法
2016/02/03 PHP
Laravel5权限管理方法详解
2016/07/26 PHP
javascript下阻止表单重复提交、防刷新、防后退
2007/08/17 Javascript
javascript来定义类的规范小结
2010/11/19 Javascript
jQuery EasyUI API 中文文档 - Dialog对话框
2011/11/15 Javascript
jsonp原理及使用
2013/10/28 Javascript
JavaScript图片放大技术(放大镜)实现代码分享
2013/11/14 Javascript
ie与ff下的event事件使用介绍
2013/11/25 Javascript
Bootstrap 折叠(Collapse)插件用法实例详解
2016/06/01 Javascript
JAVA Web实时消息后台服务器推送技术---GoEasy
2016/11/04 Javascript
Vue组件开发初探
2017/02/14 Javascript
在Vue中使用this.$store或者是$route一直报错的解决
2019/11/08 Javascript
解决vuecli3中img src 的引入问题
2020/08/04 Javascript
[48:47]VGJ.S vs NB 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
[01:23:24]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第三场 2月7日
2021/03/11 DOTA
python正则表达式抓取成语网站
2013/11/20 Python
python写入xml文件的方法
2015/05/08 Python
Python 判断是否为质数或素数的实例
2017/10/30 Python
使用python装饰器计算函数运行时间的实例
2018/04/21 Python
解决PySide+Python子线程更新UI线程的问题
2019/01/11 Python
django2.2安装错误最全的解决方案(小结)
2019/09/24 Python
python实现四人制扑克牌游戏
2020/04/22 Python
Django 解决上传文件时,request.FILES为空的问题
2020/05/20 Python
python利用opencv实现颜色检测
2021/02/23 Python
html5实现图片转圈的动画效果——让页面动起来
2017/10/16 HTML / CSS
乌克兰时尚鞋子和衣服购物网站:Born2be
2018/05/24 全球购物
请写出 float x 与"零值"比较的 if 语句
2016/01/04 面试题
Linux的主要特性
2016/09/03 面试题
如何客观的进行自我评价
2013/12/17 职场文书
2015会计试用期工作总结
2014/12/12 职场文书
公司人力资源管理制度
2015/08/05 职场文书
学校教学管理制度
2015/08/06 职场文书
一次SQL查询优化原理分析(900W+数据从17s到300ms)
2022/06/10 SQL Server