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 身份验证方面的函数
Oct 11 PHP
php 阴历-农历-转换类代码
Jan 16 PHP
PHP中使用foreach和引用导致程序BUG的问题介绍
Sep 05 PHP
discuz免激活同步登入代码修改方法(discuz同步登录)
Dec 24 PHP
php多种形式发送邮件(mail qmail邮件系统 phpmailer类)
Jan 22 PHP
PHP中调用C/C++制作的动态链接库的教程
Mar 10 PHP
PHP数学运算与数据处理实例分析
Apr 01 PHP
php基于闭包实现函数的自调用(递归)实例分析
Nov 11 PHP
IOS 开发之NSDictionary转换成JSON字符串
Aug 14 PHP
PHP操作MySQL中BLOB字段的方法示例【存储文本与图片】
Sep 15 PHP
详解PHP 二维数组排序保持键名不变
Mar 06 PHP
PHP使用PDO操作sqlite数据库应用案例
Mar 07 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定义函数代码
2015/02/26 PHP
一张表搞清楚php is_null、empty、isset的区别
2015/07/07 PHP
详谈php ip2long 出现负数的原因及解决方法
2017/04/05 PHP
yii2安装详细流程
2018/05/23 PHP
PHP面向对象程序设计之对象的遍历操作示例
2019/06/12 PHP
php报错502badgateway解决方法
2019/10/11 PHP
jQuery Validation插件remote验证方式的Bug解决
2010/07/01 Javascript
javascript深入理解js闭包
2010/07/03 Javascript
浅谈JavaScript中的分支结构
2016/07/01 Javascript
避免jQuery名字冲突 noConflict()方法
2016/07/30 Javascript
nodejs中全局变量的实例解析
2017/03/07 NodeJs
jQuery实现简单的抽奖游戏
2017/05/05 jQuery
基于JavaScript实现无限加载瀑布流
2017/07/21 Javascript
利用纯JS实现像素逐渐显示的方法示例
2017/08/14 Javascript
async/await地狱该如何避免详解
2018/05/10 Javascript
Vue 配合eiement动态路由,权限验证的方法
2018/09/26 Javascript
浅谈让你的代码更简短,更整洁,更易读的ES6小技巧
2018/10/25 Javascript
深入解析Vue源码实例挂载与编译流程实现思路详解
2019/05/05 Javascript
Python实战小程序利用matplotlib模块画图代码分享
2017/12/09 Python
解决Python requests库编码 socks5代理的问题
2018/05/07 Python
Python3.4 splinter(模拟填写表单)使用方法
2018/10/13 Python
python绘制已知点的坐标的直线实例
2019/07/04 Python
python把ipynb文件转换成pdf文件过程详解
2019/07/09 Python
PyQt5基本控件使用之消息弹出、用户输入、文件对话框的使用方法
2019/08/06 Python
python3实现网页版raspberry pi(树莓派)小车控制
2020/02/12 Python
详解Python yaml模块
2020/09/23 Python
利用python清除移动硬盘中的临时文件
2020/10/28 Python
利用HTML5+CSS3实现3D转换效果实例详解
2017/05/02 HTML / CSS
Java程序开发中如何应用线程
2016/03/03 面试题
超市实习总结自我鉴定
2013/09/19 职场文书
单位领导证婚词
2014/01/14 职场文书
新党章心得体会
2014/09/04 职场文书
微信小程序和php的登录实现
2021/04/01 PHP
新手入门Jvm-- JVM对象创建与内存分配机制
2021/06/18 Java/Android
Nginx 常用配置
2022/05/15 Servers
Go gorilla securecookie库的安装使用详解
2022/08/14 Golang