이틀 전 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 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.