本篇內(nèi)容主要講解“怎么用PHP實(shí)現(xiàn)簡(jiǎn)單的Socks5 Server”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么用PHP實(shí)現(xiàn)簡(jiǎn)單的Socks5 Server”吧!
創(chuàng)新互聯(lián)是專業(yè)的涉縣網(wǎng)站建設(shè)公司,涉縣接單;提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行涉縣網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
利用 Phalcon7 的異步功能實(shí)現(xiàn),完整源碼 https://github.com/dreamsxin/cphalcon7/blob/master/examples/async/Socks5Server.php。
concurrency = max(1, $concurrency); $this->channel = new \Phalcon\Async\Channel($capacity); $this->context = \Phalcon\Async\Context::background(); } public function close(?\Throwable $e = null): void { $this->count = \PHP_INT_MAX; $this->channel->close($e); } public function submit(callable $work, $socket, ...$args): \Phalcon\Async\Awaitable { if ($this->count < $this->concurrency) { $this->count++; Socks5Server::info('Pool count '.$this->count); \Phalcon\Async\Task::asyncWithContext($this->context, static function (iterable $it) { try { foreach ($it as list ($defer, $context, $work, $socket, $args)) { try { $defer->resolve($context->run($work, $socket, ...$args)); } catch (\Throwable $e) { Socks5Server::err($e->getMessage()); $defer->fail($e); } finally { } } } catch (\Throwable $e) { Socks5Server::err($e->getMessage()); } finally { --$this->count; } }, $this->channel->getIterator()); } $this->channel->send([ $defer = new \Phalcon\Async\Deferred(), \Phalcon\Async\Context::current(), $work, $socket, $args ]); return $defer->awaitable(); } } class Socks5Server { static public $debug = false; private $server; private $host; private $port; private $clients; public function __construct($host, $port, callable $callback = NULL, int $concurrency = 1, int $capacity = 0) { $this->host = $host; $this->port = $port; $this->callback = $callback; $this->pool = new Pool($concurrency, $capacity); } public function start() { $callback = $this->callback; $ws = $this; $worker = static function ($socket) use ($ws, $callback) { $socket->status = Socks5::STATUS_INIT; $socket->is_closing = false; try { $buffer = ''; while (!$socket->is_closing && null !== ($chunk = $socket->read())) { $buffer .= $chunk; switch ($socket->status) { case Socks5::STATUS_INIT: /** * 協(xié)商版本以及驗(yàn)證方式 * +---------+-----------------+------------------+ * |協(xié)議版本 |支持的驗(yàn)證式數(shù)量 |驗(yàn)證方式 | * +---------+-----------------+------------------+ * |1個(gè)字節(jié) |1個(gè)字節(jié) |1種式占一個(gè)字節(jié) | * +---------+-----------------+------------------+ * |0x05 |0x02 |0x00,0x02 | * +---------+-----------------+------------------+ */ /** * 0x00 無驗(yàn)證需求 * 0x01 通用安全服務(wù)應(yīng)用程序接口(GSSAPI) * 0x02 用戶名/密碼(USERNAME/PASSWORD) * 0x03 至 X’7F’ IANA 分配(IANA ASSIGNED) * 0x80 至 X’FE’ 私人方法保留(RESERVED FOR PRIVATE METHODS) * 0xFF 無可接受方法(NO ACCEPTABLE METHODS) */ $authtypes = Socks5::parseAuth($buffer); if ($authtypes === false) { $socket->is_closing = true; break; } if ($authtypes === true) { // continue break; } $socket->status = Socks5::STATUS_ADDR; $socket->write("\x05\x00"); // TODO: 暫時(shí) $buffer = ''; break; case Socks5::STATUS_AUTH: if ($callback && \is_callable($callback)) { // TODO } break; case Socks5::STATUS_ADDR: self::info('Socks5::STATUS_ADDR'); /** * 建立代理連接 * +----------+------------+---------+-----------+-----------------------+------------+ * |協(xié)議版本 |請(qǐng)求的類型 |保留字段 |地址類型 |地址數(shù)據(jù) |地址端口 | * +----------+------------+---------+-----------+-----------------------+------------+ * |1個(gè)字節(jié) |1個(gè)字節(jié) |1個(gè)字節(jié) |1個(gè)字節(jié) |變長(zhǎng) |2個(gè)字節(jié) | * +----------+------------+---------+-----------+-----------------------+------------+ * |0x05 |0x01 |0x00 |0x01 |0x0a,0x00,0x01,0x0a |0x00,0x50 | * +----------+------------+---------+-----------+-----------------------+------------+ */ /** * 請(qǐng)求類型 * CONNECT : 0x01, 建立代理連接 * BIND : 0x02,告訴代理服務(wù)器監(jiān)聽目標(biāo)機(jī)器的連接,也就是讓代理服務(wù)器創(chuàng)建socket監(jiān)聽來自目標(biāo)機(jī)器的連接。FTP這類需要服務(wù)端主動(dòng)聯(lián)接客戶端的的應(yīng)用場(chǎng)景。 * 1. 只有在完成了connnect操作之后才能進(jìn)行bind操作 * 2. bind操作之后,代理服務(wù)器會(huì)有兩次響應(yīng), 第一次響應(yīng)是在創(chuàng)建socket監(jiān)聽完成之后,第二次是在目標(biāo)機(jī)器連接到代理服務(wù)器上之后。 * UDP ASSOCIATE : 0x03, udp 協(xié)議請(qǐng)求代理。 */ if (strlen($buffer) < 2) { break; } $cmd = ord($buffer[1]); if ($cmd != Socks5::CMD_CONNECT) { self::err("Bad command ".$cmd); $socket->is_closing = true; break; } $headers = Socks5::parseAddr($buffer); if (!$headers) { self::err('Error header'); $socket->is_closing = true; break; } if ($headers === true) { // continue break; } /** * 數(shù)據(jù)包轉(zhuǎn)發(fā) * +----+------+------+----------+----------+----------+ * |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | * +----+------+------+----------+----------+----------+ * | 2 | 1 | 1 | Variable | 2 | Variable | * +----+------+------+----------+----------+----------+ */ $buffer = substr($buffer, $headers[3]); $socket->status = Socks5::STATUS_CONNECTING; if (!in_array($headers[0], [Socks5::TYPE_IPV4, Socks5::TYPE_HOST, Socks5::TYPE_IPV6])) { self::err('Addr type error'); $socket->is_closing = true; break; } $tls = NULL; if ($headers[2] == 443) { $tls = new \Phalcon\Async\Network\TlsClientEncryption(); $tls = $tls->withAllowSelfSigned(true); } $socket->client = \Phalcon\Async\Network\TcpSocket::connect($headers[1], $headers[2], $tls); $socket->write(Socks5::REPLY_ADDR); $socket->status = Socks5::STATUS_STREAM; \Phalcon\Async\Task::async(function ($client) use ($socket) { while (!$socket->is_closing && null !== ($chunk = $client->read())) { $socket->write($chunk); } }, $socket->client); break; case Socks5::STATUS_STREAM: self::info('Socks5::STATUS_STREAM'); $socket->client->write($buffer); $buffer = ''; break; } } } catch (\Throwable $e) { self::err($e->getMessage()); } finally { $socket->close(); } }; try { $this->server = \Phalcon\Async\Network\TcpServer::listen($this->host, $this->port); echo Phalcon\Cli\Color::info('start server listen:'.$this->host.':'.$this->port).PHP_EOL; while (true) { $socket = $this->server->accept(); if ($socket === false) { continue; } $this->pool->submit($worker, $socket); } } catch (\Throwable $e) { self::err($e->getMessage()); } finally { if ($this->server) { $this->server->close(); } } } static public function info($message) { if (self::$debug) { echo Phalcon\Cli\Color::info($message).PHP_EOL; } } static public function err($message) { echo Phalcon\Cli\Color::error($message).PHP_EOL; } } $opts = new \Phalcon\Cli\Options('Websocket CLI'); $opts->add([ 'type' => \Phalcon\Cli\Options::TYPE_STRING, 'name' => 'server', 'shortName' => 's', 'required' => false, // 可選,需要用=號(hào)賦值 'help' => "address" ]); $opts->add([ 'type' => \Phalcon\Cli\Options::TYPE_INT, 'name' => 'port', 'shortName' => 'p', 'required' => false, 'help' => "port" ]); $opts->add([ 'type' => \Phalcon\Cli\Options::TYPE_BOOLEAN, 'name' => 'concurrency', 'shortName' => 'c', 'required' => false ]); $opts->add([ 'type' => \Phalcon\Cli\Options::TYPE_BOOLEAN, 'name' => 'capacity', 'shortName' => 'C', 'required' => false ]); $opts->add([ 'type' => \Phalcon\Cli\Options::TYPE_BOOLEAN, 'name' => 'debug', 'shortName' => 'v', 'required' => false, 'help' => "enable debug" ]); $vals = $opts->parse(); if ($vals === false ) { exit; } /** * 運(yùn)行 php websocket-server.php */ if (isset($vals['debug'])) { Socks5Server::$debug = true; echo Phalcon\Cli\Color::info('Use debug mode').PHP_EOL; } $sserver = new Socks5Server(\Phalcon\Arr::get($vals, 'server', '0.0.0.0'), \Phalcon\Arr::get($vals, 'port', 10002), function($socket, $status, $data) { // TODO }, \Phalcon\Arr::get($vals, 'concurrency', 500), \Phalcon\Arr::get($vals, 'capacity', 1)); $sserver->start();
到此,相信大家對(duì)“怎么用PHP實(shí)現(xiàn)簡(jiǎn)單的Socks5 Server”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!