100多行PHP代码实现socks5代理服务器[2]
对swoole理解不深,不知道怎么处理socketshutdown只关闭读/写这样,还有就是连接超时,读写超时这种怎么处理。在网上看到作者说要用定时器,感觉好麻烦,所以,这次的代理,虽然个人用,一般不会有什么问题,但离产品级的代理,还有段路要走。
如果要利用多核,就使用process模式,设置worker个数为cpu数量即可。
<?php classClient { public$connected=true; public$data=''; public$remote=null; public$status=0; } classServer { public$clients=[]; publicfunctionstart() { $server=newswoole_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'=>'socks5_server.log', ]); $server->on('connect',[$this,'onConnect']); $server->on('receive',[$this,'onReceive']); $server->on('close',[$this,'onClose']); $server->start(); } publicfunctiononConnect($server,$fd,$fromID) { $this->clients[$fd]=newClient(); } publicfunctiononReceive($server,$fd,$fromID,$data) { ($this->clients[$fd])->data.=$data; $this->parse($server,$fd); } publicfunctiononClose($server,$fd,$fromID) { $client=$this->clients[$fd]; $client->connected=false; } privatefunctionparse($server,$fd) { $client=$this->clients[$fd]; switch($client->status){ case0:{ if(strlen($client->data)>=2){ $request=unpack('c*',substr($client->data,0,2)); if($request[1]!==0x05){ echo'协议不正确:'.$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; } } } case1:{ if(strlen($client->data)<5) break; $request=unpack('c*',$client->data); $aType=$request[4]; if($aType===0x03){//domain $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); }elseif($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); }else{ echo'不支持的atype:'.$aType,PHP_EOL; $server->close($fd); break; } $remote=newswoole_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"); $client->status=2; $client->remote=$remote; }); $remote->on("error",function(swoole_client$cli)use($server,$fd){ //$server->send($fd,"");//todo连接不上remote echo'connecttoremoteerror.',PHP_EOL; $server->close($fd); }); $remote->on('receive',function($cli,$data)use($server,$fd,$client){ if(!$client->connected){ echo'connectionhasbeenclosed.',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当host为空时的处理。貌似不存在的域名都解析成了本机的外网ip,奇怪 if(empty($ip)||empty($host)){ echo"host:{$host},ip:{$ip}\n"; $server->close($fd); return; } $remote->connect($ip,$port); }); }else{ $remote->connect($domain,$port); } } case2:{ if(strlen($client->data)===0){ break; } if($client->remote===null){ echo'remoteconnectionhasbeenclosed.',PHP_EOL; break; } $sendByteCount=$client->remote->send($client->data); if($sendByteCount===false||$sendByteCount<strlen($client->data)){ echo'datalength:',strlen($client->data),'sendbytecount:',$sendByteCount,PHP_EOL; echo$client->data,PHP_EOL; $server->close($fd); } $client->data=''; } } } } (newServer())->start();