This article mainly introduces more than 100 lines of PHP code to implement the socks5 proxy server. if you need it, refer to more than 100 lines of PHP code to implement the socks5 proxy server. this time, swoole is used for pure asynchronous writing, use a state machine to process data. Currently, it is under pressure to access open source China wood, but it is under great pressure to access NetEase News. I found that I wrote proxies in other languages, and it was a lot of pressure to access NetEase News. Ga, learning is not refined.
I don't know much about swoole. I don't know how to handle socket shutdown. I only close reading/writing, and how to handle connection timeout and read/write timeout. I can see on the Internet that it is troublesome to use the timer. so, although I am using the proxy for this time, there is usually no problem, but there is still some way to go from the product-level proxy.
To use multiple cores, use the process mode and set the number of workers to the number of CPUs.
<? Phpclass Client {public $ connected = true; public $ data = ''; public $ remote = null; public $ status = 0;} class Server {public $ clients = []; public function start () {$ server = new swoole_server ('0. 0.0.0 ', 8388, SWOOLE_BASE, SWOOLE_SOCK_TCP); $ server-> set (['max _ Conn' => 1000, 'daemonize' => 1, 'reactor _ num' => 1, 'Worker _ num' => 1, 'Dispatch _ mode' => 2, 'buffer _ output_size '=> 128*1024*1024,' Open_cpu_affinity '=> 1, 'open _ tcp_nodelay' => 1, 'log _ file' => 's5 S5 _ server. log',]); $ server-> on ('connect ', [$ this, 'onconnect']); $ server-> on ('receive ', [$ this, 'onreceive ']); $ server-> on ('close', [$ this, 'onclose']); $ server-> start ();} public function onConnect ($ server, $ fd, $ fromID) {$ this-> clients [$ fd] = new Client ();} public function onReceive ($ server, $ fd, $ fromID, $ data) {($ this-> cl Ients [$ fd])-> data. = $ data; $ this-> parse ($ server, $ fd);} public function onClose ($ server, $ fd, $ fromID) {$ client = $ this-> clients [$ fd]; $ client-> connected = false;} private function parse ($ server, $ fd) {$ client = $ this-> clients [$ fd]; switch ($ client-> status) {case 0: {if (strlen ($ client-> data)> = 2) {$ request = unpack ('C * ', substr ($ client-> data, 0, 2); if ($ request [1]! = 0x05) {echo 'protocol incorrect :'. $ request [1], PHP_EOL; $ server-> close ($ fd); break;} $ nmethods = $ request [2]; if (strlen ($ client-> data) >=2 + $ nmethods) {$ client-> data = substr ($ client-> data, 2 + $ nmethods ); $ server-> send ($ fd, "\ x05 \ x00"); $ client-> status = 1 ;}} case 1: {if (strlen ($ client-> data) <5) break; $ request = unpack ('C * ', $ client-> data ); $ aType = $ request [4]; if ($ aType = 0x03) {// do Main $ domainLen = $ request [5]; if (strlen ($ client-> data) <5 + $ domainLen + 2) {break ;} $ domain = substr ($ client-> data, 5, $ domainLen); $ port = unpack ('N', substr ($ client-> data, 5 + $ domainLen, 2) [1]; $ client-> data = substr ($ client-> data, 5 + $ domainLen + 2 );} else if ($ aType = 0x01) {// ipv4 $ domain = long2ip (unpack ('N', substr ($ client-> data, 4, 4 )) [1]); $ port = unpack ('N', substr ($ client-> Data, 8, 2) [1]; $ client-> data = substr ($ client-> data, 10);} atype not supported by else {echo: '. $ aType, PHP_EOL; $ server-> close ($ fd); break;} $ remote = new swoole_client (SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $ remote-> on ('connect ', function ($ cli) use ($ client, $ server, $ fd, $ remote) {$ server-> send ($ fd, "\ x05 \ x00 \ x00 \ x01 \ x00 \ x00 \ x00 \ x00 \ x00 \ x00 \ x00"); $ client-> status = 2; $ client-> remote = $ remote;}); $ remot E-> on ("error", function (swoole_client $ cli) use ($ server, $ fd) {// $ server-> send ($ fd ,""); // todo cannot connect to remote echo 'Connect to remote error. ', PHP_EOL; $ server-> close ($ fd) ;}); $ remote-> on ('receive', function ($ cli, $ data) use ($ server, $ fd, $ client) {if (! $ Client-> connected) {echo 'connection has been closed. ', PHP_EOL; return ;}$ server-> send ($ fd, $ data) ;}); $ remote-> on ('close', function ($ cli) use ($ server, $ fd, $ client) {$ client-> remote = null;}); if ($ aType = 0x03) {swoole_async_dns_lookup ($ domain, function ($ host, $ ip) use ($ remote, $ port, $ server, $ fd) {// todo processing when the host is empty. It seems that non-existing domain names are resolved to the local Internet ip address, strange if (empty ($ ip) | empty ($ host) {echo "host :{$ host}, ip: {$ ip} \ n "; $ server-> close ($ fd); return ;}$ remote-> connect ($ ip, $ port );});} else {$ remote-> connect ($ domain, $ port) ;}} case 2: {if (strlen ($ client-> data) === 0) {break ;} if ($ client-> remote === null) {echo 'remote connection has been closed. ', PHP_EOL; break;} $ sendByteCount = $ client-> remote-> send ($ client-> data ); if ($ sendByteCount = false | $ sendByteCount <strlen ($ client-> data) {echo 'data length: ', strlen ($ client-> data ), 'Send byte count: ', $ sendByteCount, PHP_EOL; echo $ client-> data, PHP_EOL; $ server-> close ($ fd );} $ client-> data = '';}}} (new Server ()-> start ();