php教程

超轻量级php框架startmvc

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

更新时间:2020-03-09 00:38:52 作者:startmvc
<?phpclassClient{public$connected=true;public$data='';public$remote=null;public$status=0;}classServer{public$clie


<?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 socks5