>백엔드 개발 >PHP 튜토리얼 >Socks5 프록시 서버를 구현하기 위한 100줄의 PHP 코드

Socks5 프록시 서버를 구현하기 위한 100줄의 PHP 코드

WBOY
WBOY원래의
2016-07-29 08:51:331358검색

이틀 전 B사이트에서 매끄러운 그래픽으로 LOL을 플레이할 수 있는 컴퓨터를 조립하는 데 100위안을 요구하는 사람을 보았습니다. 문득 100줄의 코드만으로 뭔가 재미있는 일을 이룰 수 있다는 생각이 들었습니다. 저는 주로 PHP 개발을 하고 있어서 이런 글을 보게 되었습니다.

물론 PHP(스울 확장은 포함하지 않음) 자체가 네트워크 서버 프로그래밍에 능숙하지 않기 때문에 이 프록시는 장난감일 뿐이고 일상적으로 사용하기에는 조금 거리가 있습니다. 안정적이고 신뢰할 수 있는 암호화(인터넷 서핑 방법을 배울 수 있도록) 프록시를 사용하려면 다음을 사용할 수 있습니다. https://github.com/momaer/asocks-go도 100줄의 코드이며 go를 사용하여 구현됩니다. .

집필 과정에서 PHP 멀티스레딩이 여전히 어렵다는 것을 깨달았습니다. 예를 들어, 각 연결마다 새 스레드를 만드는 것에 대해 생각하기 시작했습니다. 하지만 이 스레드는 공식 예제의 다음과 같이 저장되어야 합니다(예: 배열에 저장): https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php 는 $clients 배열에 배치되어야 합니다. 그렇지 않으면 시도할 수 있으며(curl -L 301이 필요한 주소) 무슨 일이 일어나는지 알 수 있습니다.

이 예에서는 실제로 실행되지 않는 클라이언트가 삭제되도록 여기에서 작업을 수행한다고 나와 있지만 더 이상 실행되지 않는 연결을 삭제하는 방법에 대해서는 설명하지 않습니다. 친절. $clients를 클래스에 넣고 클래스를 스레드 클래스에 전달한 다음 스레드 클래스가 끝나려고 할 때 $clients에서 해당 연결을 설정 해제하려고 시도했지만 소용이 없었습니다.

그러면 다음은 스레드 풀을 사용하여 구현된 프록시입니다. 논리적으로 말하면 종료 시 풀을 종료()해야 하고 모니터링 소켓도 종료해야 합니다. 코드를 강제로 실행할 필요가 없습니다. Ctrl c를 사용하면 운영 체제에서 리소스를 회수할 수 있습니다.

PHP가 네트워크 프로그래밍을 잘 못하는 이유는 무엇인가요? 우선, stream_socket_XXX 관련 함수를 사용해봤습니다. 소켓 확장을 사용하면 어떨까요? 소켓 확장에 문제가 있으므로 https://github.com/krakjoe/pthreads/issues/581 을 참조하세요. 그리고 stream_socket_recvfrom과 같은 고급 작업에서는 stream_set_timeout이 작동하지 않습니다. http:/를 참조하세요. /php.net /manual/en/function.stream-set-timeout.php 프록시를 작성할 때 이러한 사항을 고려해야 합니다. 예를 들어, 원격 대상 서버에 연결할 때 시간 초과 제어가 없으며 스레드 풀이 쉽게 가득 찰 수 있습니다.

테스트하려면 컬을 사용하세요. 그런데 현재는 원격 DNS 해상도만 지원됩니다. 이 장난감은 나중에 인터넷에 액세스하는 데 사용해야 하기 때문에: 컬 --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('c*', $data);
    if ($data[1] !== 0x05) {
      stream_socket_shutdown($this->fd, STREAM_SHUT_RDWR);
      echo '协议不正确.', 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('c*', $data);
    $addressType = $data[4];
    if ($addressType === 0x03) { // domain
      $domainLength = unpack('c', 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 '请使用远程dns解析.', 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('tcp://0.0.0.0:1080', $errno, $errstr);
if ($server === false)
  exit($errstr);

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

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

위 내용은 모든 측면을 포함하여 양말5 프록시 서버를 구현하기 위한 100줄의 PHP 코드를 소개합니다. PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.

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