>  기사  >  백엔드 개발  >  PHP는 단순히 양말5 프록시 서버를 구현합니다.

PHP는 단순히 양말5 프록시 서버를 구현합니다.

*文
*文원래의
2017-12-29 18:58:4416006검색

100줄의 코드로 (간단히) 어떤 재미있는 일을 이룰 수 있나요? 이 기사에서는 예제를 사용하여 100줄의 PHP 코드로 양말5 프록시 서버 모듈을 간단하게 구현하는 방법을 보여줍니다. 이것이 모든 사람에게 도움이 되기를 바랍니다.

물론 PHP(Swoole Extension은 포함하지 않음) 자체가 네트워크 서버 프로그래밍에 능숙하지 않기 때문에 이 에이전트는 단지 장난감일 뿐이고 일상적으로 사용하기에는 조금 거리가 있습니다.

작성 과정에서 PHP 멀티스레딩이 여전히 어렵다는 것을 알았습니다. 예를 들어, 각 연결마다 새 스레드를 만들고 싶었습니다. 하지만 이 스레드는 $clients 배열에 저장(예: 배열에 저장)되어야 합니다. 그렇지 않으면 시도(curl -L 301이 필요한 주소)하면 무슨 일이 일어나는지 알 수 있습니다.

다음은 스레드 풀을 사용하여 구현된 에이전트입니다. 논리적으로 말하면 종료 시 풀을 종료()해야 하고 청취 소켓도 종료해야 합니다. 그러나 수백 줄의 코드가 있습니다. 강제로 실행할 필요는 없습니다. ctrl + c를 사용하면 운영 체제에서 리소스를 회수할 수 있습니다.

PHP가 네트워크 프로그래밍을 잘 못하는 이유는 무엇인가요? 우선, stream_socket_XXX 관련 함수를 사용했습니다. 소켓 확장을 사용하면 어떨까요? 소켓 확장에 문제가 있기 때문입니다. 그러나 stream_set_timeout은 stream_socket_recvfrom과 같은 고급 작업에는 영향을 주지 않습니다. 그리고 에이전트를 작성할 때 이러한 사항을 고려해야 합니다. 예를 들어, 원격 대상 서버에 연결할 때 시간 초과 제어가 없으며 스레드 풀이 쉽게 가득 찰 수 있습니다.

테스트에는 컬을 사용하세요. 그런데 현재는 원격 DNS 해상도만 지원됩니다. 왜일까요? 이 장난감을 사용하면 나중에 Hedou가 인터넷에 액세스하는 방법을 배울 수 있기 때문입니다. 컬 --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));
}

관련 권장 사항:

php는 swoole을 사용하여 실시간으로 클라이언트 데이터를 업데이트합니다

Windows에서 swoole을 설치하는 단계는 무엇입니까?

PHP 학습 CURL 크롤러 예제

위 내용은 PHP는 단순히 양말5 프록시 서버를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.