Two days ago in B station to see a small group of paper 100 to assemble a computer hit LOL quality fluent, whim 100 lines of code can (simple) realize what fun. I mainly do PHP development, so there is the article.
Of course, because PHP (not swoole expansion) itself is not good at doing network server programming, so this agent, just a toy, away from the day-to-day use of a little distance. If you want to use a stable and reliable encryption (so you can learn the Web) agent, you can use this: Https://github.com/momaer/asocks-go is also 100来 line code using GO implementation.
Writing in the process of discovering PHP multithreading is still difficult. For example, I started thinking about creating a new thread for each connection. But this thread has to be saved (for example, to an array), as in the official example: https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php to put it in the $clients array, otherwise, you try (curl-l a 301 address) to know what happened.
This example says in the "real world", do something-ensure clients not running are destroyed but how to destroy a connection that no longer runs does not speak. Well. I tried to put the $clients into a class, pass the class to the thread class, and then $clients the corresponding connection to the unset at the end of the thread class, without fruit.
That, the following is the use of the thread pool to achieve the agent, according to the truth, when the pool to exit shutdown (), listening to the socket also to shutdown, but hundred lines of code, do not reluctantly, with the CTRL + C, let the operating system to reclaim resources.
What is the embodiment of PHP not good at network programming? First I use the stream_socket_xxx related function, why not use the socket extension? Because of a problem with the socket extension, see: https://github.com/krakjoe/pthreads/issues/581 and Stream_set_timeout to stream_socket_ Recvfrom These advanced operations, which do not work, see : http://php.net/manual/en/function.stream-set-timeout.php and these, you need to consider when writing an agent. For example, when connecting to a remote target server, there is no timeout control, it is easy to run the thread pool full.
test, the use of curl can be, right, currently only support remote DNS resolution, why?" Because this toy late but want to realize wo Dou learn to surf the net of yo: 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;
The 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 ' protocol is incorrect. ', 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 ' Please use remote DNS resolution. ', 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;
$pipe 1 = new Pipe ($remote, $this->fd);
$pipe 2 = new Pipe ($this->fd, $remote);
$pool->submit ($pipe 1);
$pool->submit ($pipe 2);
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 (worker::class);
$pool = new Pool (' Proxyworker ', [$pipePool]); for (;;)
{$fd = @stream_socket_accept ($server, 60);
if ($FD = = false) continue;
$pool->submit (New Client ($FD)); }