Heim  >  Artikel  >  Backend-Entwicklung  >  PHP implementiert einfach den Socks5-Proxyserver

PHP implementiert einfach den Socks5-Proxyserver

*文
*文Original
2017-12-29 18:58:4416006Durchsuche

1Welche lustige Sache kann (einfach) mit 00 Zeilen Code erreicht werden? Dieser Artikel zeigt ein Beispiel für die einfache Implementierung des Socks5-Proxyservermoduls mit 100 Zeilen PHP-Code. Ich hoffe, dass es für alle hilfreich ist.

Da PHP (Swoole-Erweiterung nicht mitgezählt) selbst nicht gut in der Programmierung von Netzwerkservern ist, ist dieser Agent natürlich nur ein Spielzeug und etwas weit weg aus dem täglichen Gebrauch.

Während des Schreibprozesses stellte ich fest, dass PHP-Multithreading immer noch schwierig ist. Ich begann zum Beispiel darüber nachzudenken, für jede Verbindung einen neuen Thread zu erstellen. Dieser Thread muss jedoch im Array $clients gespeichert werden (z. B. in einem Array). Andernfalls können Sie es versuchen (curl -L eine Adresse, die 301 erfordert) und Sie werden wissen, was passiert.

Dann ist das Folgende ein Proxy, der mithilfe eines Thread-Pools implementiert wird. Logischerweise muss der Pool beim Beenden heruntergefahren werden, und der Überwachungs-Socket muss ebenfalls heruntergefahren werden Aber bei hundert Zeilen Code besteht keine Notwendigkeit, das Betriebssystem widerwillig mit Strg + C Ressourcen zurückfordern zu lassen.

Warum ist PHP nicht gut in der Netzwerkprogrammierung? Zunächst habe ich die stream_socket_XXX-bezogenen Funktionen verwendet. Warum nicht die Socket-Erweiterung verwenden? Weil es ein Problem mit der Socket-Erweiterung gibt. Allerdings hat stream_set_timeout keine Auswirkung auf erweiterte Vorgänge wie stream_socket_recvfrom. Und diese müssen beim Schreiben eines Agenten berücksichtigt werden. Wenn Sie beispielsweise eine Verbindung zu einem Remote-Zielserver herstellen, gibt es keine Zeitüberschreitungskontrolle und der Thread-Pool kann leicht voll werden.

Zum Testen verwenden Sie einfach Curl. Derzeit wird nur die Remote-DNS-Auflösung unterstützt. Da dieses Spielzeug später für den Zugriff auf das Internet verwendet werden muss: curl --socks5-hostname 127.0.0.1:1080 http://ip.cn

Class Pipe extends Threaded
{
  private $client;
  private $remote;
  public function __construct($client, $remote) 
  {
    $this->client = $client;
    $this->remote = $remote; 
  }
  public function run()
  {
    for ( ; ; ) {
        $data = stream_socket_recvfrom($this->client, 4096);
        if ($data === false || strlen($data) === 0) {
          break;
        } 
        $sendBytes = stream_socket_sendto($this->remote, $data);
        if ($sendBytes <= 0) {
          break;
        }
    }
    stream_socket_shutdown($this->client, STREAM_SHUT_RD);
    stream_socket_shutdown($this->remote, STREAM_SHUT_WR);
  }
}

Class Client extends Threaded
{
  public $fd;
  public function __construct($fd)
  {
    $this->fd = $fd; 
  }

  public function run()
  {
    $data = stream_socket_recvfrom($this->fd, 2);
    $data = unpack(&#39;c*&#39;, $data);
    if ($data[1] !== 0x05) {
      stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);
      echo &#39;协议不正确.&#39;, PHP_EOL;
      return;
    }
    $nmethods = $data[2];
    $data = stream_socket_recvfrom($this->fd, $nmethods);
    stream_socket_sendto($this->fd, "\x05\x00");
    $data = stream_socket_recvfrom($this->fd, 4);
    $data = unpack(&#39;c*&#39;, $data);
    $addressType = $data[4];
    if ($addressType === 0x03) { // domain
      $domainLength = unpack(&#39;c&#39;, stream_socket_recvfrom($this->fd, 1))[1];
      $data = stream_socket_recvfrom($this->fd, $domainLength + 2);
      $domain = substr($data, 0, $domainLength);
      $port = unpack("n", substr($data, -2))[1];
    } else {
      stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);
      echo &#39;请使用远程dns解析.&#39;, PHP_EOL;
    }

    stream_socket_sendto($this->fd, "\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00");
    echo "{$domain}:{$port}", PHP_EOL;
    $remote = stream_socket_client("tcp://{$domain}:{$port}");
    if ($remote === false) {
      stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);
      return;
    }

    $pool = $this->worker->pipePool;

    $pipe1 = new Pipe($remote, $this->fd);
    $pipe2 = new Pipe($this->fd, $remote);

    $pool->submit($pipe1);
    $pool->submit($pipe2);
  }
}

class ProxyWorker extends Worker
{
  public $pipePool;
  public function __construct($pipePool)
  {
    $this->pipePool = $pipePool;
  }
}

$server = stream_socket_server(&#39;tcp://0.0.0.0:1080&#39;, $errno, $errstr);
if ($server === false)
  exit($errstr);

$pipePool = new Pool(200, Worker::class);
$pool = new Pool(50, &#39;ProxyWorker&#39;, [$pipePool]);

for( ; ; ) {
  $fd = @stream_socket_accept($server, 60);
  if ($fd === false)
    continue;
  $pool->submit(new Client($fd));
}

Verwandte Empfehlungen:

PHP verwendet Swoole, um Client-Daten in Echtzeit zu aktualisieren

Was sind die Schritte, um Swoole unter Windows zu installieren?

Beispiel für einen PHP-Lern-CURL-Crawler

Das obige ist der detaillierte Inhalt vonPHP implementiert einfach den Socks5-Proxyserver. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn