加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

100行PHP代码实现socks5代理服务器

发布时间:2020-12-12 21:28:33 所属栏目:PHP教程 来源:网络整理
导读:p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,255,255); text-indent: 0px; margin: 8px 0px; padding-left: 0px; letter-spacing: 0px; padding-right: 0px; font: 12px/22px 'Microsoft

<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,255,255); text-indent: 0px; margin: 8px 0px; padding-left: 0px; letter-spacing: 0px; padding-right: 0px; font: 12px/22px 'Microsoft YaHei',Verdana,sans-serif,宋体; word-wrap: break-word; white-space: normal; color: rgb(51,51,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">前两天在B站上看到一个小伙纸100元组装个电脑打LOL画质流畅,突发奇想100行代码能(简单)实现个啥好玩的。我主要是做php开发的,于是就有了本文。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">当然,由于php(不算swoole扩展)本身不擅长做网络服务端编程,所以这个代理,只是个玩具,离日常使用有点距离。如果想使用稳定可靠的加密(所以能禾斗学上网)代理,可以用这个:<a style="padding-bottom: 0px; margin: 0px; outline-style: none; outline-color: invert; padding-left: 0px; outline-width: 0px; padding-right: 0px; color: rgb(255,131,115); font-size: 12px; padding-top: 0px" href="https://github.com/momaer/asocks-go"&gt;https://github.com/momaer/asocks-go也是100来行代码使用go实现。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">写的过程中发现php多线程还是难的。比如我开始想每个连接新建一个线程。但这个线程得保存起来(比如保存到数组),比如官方例子中的这个:<a style="padding-bottom: 0px; margin: 0px; outline-style: none; outline-color: invert; padding-left: 0px; outline-width: 0px; padding-right: 0px; color: rgb(255,115); font-size: 12px; padding-top: 0px" href="https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php"&gt;https://github.com/krakjoe/pthreads/blob/master/examples/SocketServer.php<span class="Apple-converted-space">要放到$clients这个数组里,不然,你试试(curl -L一个要301的地址)就知道出现什么情况了。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">这个例子说了in the real world,do something here to ensure clients not running are destroyed 但是,如何把不再运行的连接销毁却没有讲。恩。我试了把$clients放到一个类里,把类传给线程类,然后在线程类要结束时把$clients里对应的连接给unset掉,无果。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">那,以下就是使用线程池来实现的代理,按道理讲,退出时池要shutdown(),监听socket也要shutdown的,但百行代码,就不勉强了,随着ctrl + c,就让操作系统来回收资源吧。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">php不擅长网络编程体现在哪里呢?首先我用的是stream_socket_XXX相关的函数,为啥不用socket扩展呢?因为socket扩展有问题,参见:<a style="padding-bottom: 0px; margin: 0px; outline-style: none; outline-color: invert; padding-left: 0px; outline-width: 0px; padding-right: 0px; color: rgb(255,115); font-size: 12px; padding-top: 0px" href="https://github.com/krakjoe/pthreads/issues/581"&gt;https://github.com/krakjoe/pthreads/issues/581<span class="Apple-converted-space">而stream_set_timeout对stream_socket_recvfrom这些高级操作,不起作用,参见:<a style="padding-bottom: 0px; margin: 0px; outline-style: none; outline-color: invert; padding-left: 0px; outline-width: 0px; padding-right: 0px; color: rgb(255,115); font-size: 12px; padding-top: 0px" href="http://php.net/manual/en/function.stream-set-timeout.php"&gt;http://php.net/manual/en/function.stream-set-timeout.php<span class="Apple-converted-space">而这些,在写代理时都需要考虑的。比如连接远程目标服务器时,没有超时控制,很容易就线程池跑满了。


<p style="text-align: left; padding-bottom: 0px; widows: 1; text-transform: none; background-color: rgb(255,51); word-break: break-all; word-spacing: 0px; padding-top: 0px; -webkit-text-stroke-width: 0px">测试的话,使用curl即可,对了,目前只支持远程dns解析,为啥呢?因为这个玩具后期可是要实现禾斗学上网的哟: curl --socks5-hostname 127.0.0.1:1080<span class="Apple-converted-space"><a style="padding-bottom: 0px; margin: 0px; outline-style: none; outline-color: invert; padding-left: 0px; outline-width: 0px; padding-right: 0px; color: rgb(255,115); font-size: 12px; padding-top: 0px" href="http://ip.cn/"&gt;http://ip.cn


<div class="jb51code">
<pre class="brush:php;">
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,"x05x00");
$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,$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,"x05x00x00x01x00x00x00x00x00x00");
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));
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读