Maison  >  Article  >  développement back-end  >  PHP implémente simplement le serveur proxy chaussettes5

PHP implémente simplement le serveur proxy chaussettes5

*文
*文original
2017-12-29 18:58:4416061parcourir

1Quelle chose amusante peut-on réaliser (simplement) avec 00 lignes de code ? Cet article montre un exemple de la façon d'implémenter simplement le module de serveur proxy chaussettes5 avec 100 lignes de code PHP. J'espère que cela sera utile à tout le monde.

Bien sûr, puisque PHP (sans compter l'extension swoole) lui-même n'est pas bon en programmation de serveur réseau, cet agent n'est qu'un jouet et est un peu loin de l'utilisation quotidienne.

Pendant le processus d'écriture, j'ai trouvé que le multi-threading PHP est encore difficile. Par exemple, j'ai commencé à vouloir créer un nouveau fil de discussion pour chaque connexion. Mais ce fil doit être enregistré (par exemple, enregistré dans un tableau) dans le tableau $clients. Sinon, vous pouvez essayer (curl -L une adresse qui nécessite 301) et vous saurez ce qui se passe.

Ensuite, ce qui suit est un proxy implémenté à l'aide d'un pool de threads. Logiquement, le pool doit être arrêté () lors de la sortie, et le socket d'écoute doit également être arrêté. Mais avec une centaine de lignes de code, il n'est pas nécessaire de laisser le système d'exploitation récupérer des ressources avec ctrl + c.

Pourquoi PHP n'est-il pas bon en programmation réseau ? Tout d'abord, j'ai utilisé les fonctions liées à stream_socket_XXX. Pourquoi ne pas utiliser l'extension socket ? Parce qu'il y a un problème avec l'extension du socket. Cependant, stream_set_timeout n'a aucun effet sur les opérations avancées telles que stream_socket_recvfrom. Et ceux-ci doivent être pris en compte lors de la rédaction d’un agent. Par exemple, lors de la connexion à un serveur cible distant, il n'y a aucun contrôle de délai d'attente et le pool de threads peut facilement devenir plein.

Pour les tests, utilisez simplement curl. Au fait, actuellement, seule la résolution DNS à distance est prise en charge. Pourquoi ? Parce que ce jouet devra être utilisé pour accéder à Internet ultérieurement : 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));
}

Recommandations associées :

php utilise swoole pour mettre à jour les données client en temps réel

Quelles sont les étapes pour installer swoole sous Windows ?

Exemple de robot d'exploration CURL d'apprentissage PHP

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn