100 lines of PHP code implement the socks5 Proxy Server and 100 lines of socks5
Two days ago, I saw a guy who made up 100 yuan of paper on the B station and made a smooth picture of LOL. I was wondering what fun it would be if I had 100 lines of code (simple. I am mainly engaged in php development, so I have this article.
Of course, because php (not swoole extension) itself is not good at Network Server programming, this proxy is just a toy, a little away from daily use. If you want to use a stable and reliable encryption proxy (so you can learn to access the Internet), you can use this: https://github.com/momaer/asocks-goto also use the gox code.
It is difficult to find multiple threads in php During writing. For example, I started to create a thread for each connection. But this thread has to be saved (such as saved to the array), such as the official example of this: https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php to put in the $ clients array, otherwise, you can try (curl-L an address that requires 301) to know what is going on.
In the real world, do something here to ensure clients not running are destroyed, but how to destroy a connection that is no longer running is not mentioned. Well. I tried to put $ clients into a class and pass the class to the Thread class. Then, when the Thread class is about to end, the connection corresponding to $ clients will be dropped to unset.
Then, the following is the proxy implemented by using the thread pool. In principle, shutdown () is required for the pool to exit, and shutdown is required for listening to the socket. However, if there are hundreds of lines of code, you won't be forced, with ctrl + c, let the operating system Recycle resources.
Where is php not good at network programming? First, I used stream_socket_XXX related functions. Why not use socket extension? Because of socket expansion problems, see: https://github.com/krakjoe/pthreads/issues/581 and stream_set_timeout for stream_socket_recvfrom these advanced operations, does not work, see: http://php.net/manual/en/function.stream-set-timeout.php and these, need to consider when writing the agent. For example, when connecting to the remote target server, there is no timeout control, and it is easy to run the thread pool full.
For testing, use curl. By the way, only remote dns resolution is supported currently. Why? Because this toy later but to achieve hedou learning online 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;} s Tream_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 '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 '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; $ 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: 100', $ errno, $ errstr ); if ($ server = false) exit ($ errstr); $ pipePool = new Pool (200, Worker: class); $ pool = new Pool (50, 'xyworker', [$ pipePool]); for (;) {$ fd = @ stream_socket_accept ($ server, 60); if ($ fd = false) continue; $ pool-> submit (new Client ($ fd ));}