100多行PHP代码实现socks5代理服务器[2]

<div class="jb51code">100多行PHP代码实现socks5代理服务器,这次是使用swoole纯异步来写,使用状态机来处理数据。目前用它访问开源中国木有压力,但访问网易新闻就压力山大。我发现我用别的语言写得代理,访问网易新闻都压力大。嘎嘎,学艺不精。

对swoole理解不深,不知道怎么处理socket shutdown只关闭读/写这样,还有就是连接超时,读写超时这种怎么处理。在网上看到作者说要用定时器,感觉好麻烦,所以,这次的代理,虽然个人用,一般不会有什么问题,但离产品级的代理,还有段路要走。

如果要利用多核,就使用process模式,设置worker个数为cpu数量即可。
<pre class="brush:PHP;">

<?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_outputsize' => 128 1024 1024,'opencpu_affinity' => 1,'open_tcp_nodelay' => 1,'log_file' => 'socks5_server.log',]);
$server->on('connect',[$this,'onConnect']);
$server->on('receive','onReceive']);
$server->on('close','onClose']);
$server->start();
}
public function onConnect($server,$fd,$fromID)
{
$this->clients[$fd] = new Client();
}
public function onReceive($server,$fromID,$data)
{
($this->clients[$fd])->data .= $data;
$this->parse($server,$fd);
}
public function onClose($server,$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,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',5 + $domainLen,2))[1];
$client->data = substr($client->data,5 + $domainLen + 2);
} else if ($aType === 0x01) { // ipv4
$domain = long2ip(unpack('N',4,4))[1]);
$port = unpack('n',8,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,$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.',<a href="https://www.f2er.com/tag/PHP/" target="_blank" class="keywords">PHP</a>_EOL;
 $server->close($fd);
});
$remote->on('receive',function($cli,$data) use($server,$client) {
 if (!$client->connected) {
  echo 'connection has been closed.',<a href="https://www.f2er.com/tag/PHP/" target="_blank" class="keywords">PHP</a>_EOL;
  return;
 }
 $server->send($fd,$data);
});
$remote->on('close',function($cli) use($server,$client) {
 $client->remote = null;
});
if ($aType === 0x03) {
 swoole_async_dns_lookup($domain,function($host,$ip) use($remote,$port,$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,<a href="https://www.f2er.com/tag/PHP/" target="_blank" class="keywords">PHP</a>_EOL; 
 echo $client->data,<a href="https://www.f2er.com/tag/PHP/" target="_blank" class="keywords">PHP</a>_EOL;
 $server->close($fd); 
}
$client->data = '';

}
}
}
}

(new Server())->start();

相关文章

Hessian开源的远程通讯,采用二进制 RPC的协议,基于 HTTP 传输。可以实现PHP调用Java,Python,C#等多语...
初识Mongodb的一些总结,在Mac Os X下真实搭建mongodb环境,以及分享个Mongodb管理工具,学习期间一些总结...
边看边操作,这样才能记得牢,实践是检验真理的唯一标准.光看不练假把式,光练不看傻把式,边看边练真把式....
在php中,结果输出一共有两种方式:echo和print,下面将对两种方式做一个比较。 echo与print的区别: (...
在安装好wampServer后,一直没有使用phpMyAdmin,今天用了一下,phpMyAdmin显示错误:The mbstring exte...
变量是用于存储数据的容器,与代数相似,可以给变量赋予某个确定的值(例如:$x=3)或者是赋予其它的变...