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

前端之家收集整理的这篇文章主要介绍了100行PHP代码实现socks5代理服务器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

<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,"\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,$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,"\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00");
echo "{$domain}:{$port}",<a href="/tag/PHP/" target="_blank" class="keywords">PHP</a>_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));
}

猜你在找的PHP相关文章