先来看看服务端
<?php
namespace Zhcsam\Io\Multi;
class Worker
{
// 这三个是闭包函数
public $onReceive = null;
public $onConnect = null;
public $onClose = null;
// 连接
public $socket = null;
protected $sockets = [];
public function __construct($socket_address)
{
$this->socket = stream_socket_server($socket_address);
//设置成非阻塞
stream_set_blocking($this->socket, 0);
//加入池子
$this->sockets[(int)$this->socket] = $this->socket;
// echo $socket_address."\n";
// $this->debug('stream_socket_server($socket_address);');
// $this->debug($this->socket, true);
}
// 需要处理事情
public function accept()
{
// 接收连接和处理使用
while (true) {
$read = $this->sockets;
//查看有多少资源能用,$read 是过滤后可用的资源
stream_select($read, $w, $e, 1);
foreach ($read as $socket) {
if($socket === $this->socket){
//如果是当前的资源,就新建
$this->debug("new socket");
$this->debug($socket, true);
$this->createSocket();
}else{
$this->debug("sendMessage");
$this->debug($socket, true);
$this->sendMessage($socket);
}
}
}
}
public function createSocket()
{
// 监听的过程是阻塞的
$client = stream_socket_accept($this->socket);
// is_callable判断一个参数是不是闭包
// if (is_callable($this->onConnect)) {
// // 执行函数
// ($this->onConnect)($this, $client);
// }
$this->sockets[(int) $client] = $client;
}
public function sendMessage($client)
{
$data = fread($client, 65535);
if ($data === '' || $data == false) {
// 关闭连接
fclose($client);
unset($this->sockets[(int) $client]);
return null;
}
if (is_callable($this->onReceive)) {
($this->onReceive)($this, $client, $data);
}
}
public function debug($data, $flag = false)
{
if ($flag) {
var_dump($data);
} else {
echo "==== >>>> : ".$data." \n";
}
}
// 发送信息
public function send($client, $data)
{
$response = "HTTP/1.1 200 OK\r\n";
$response .= "Content-Type: text/html;charset=UTF-8\r\n";
$response .= "Connection: keep-alive\r\n";
$response .= "Content-length: ".strlen($data)."\r\n\r\n";
$response .= $data;
echo '发送信息中。。。。';
fwrite($client, $response);
}
// 启动服务的
public function start()
{
$this->accept();
}
}
再来写调用服务端实例
<?php
require __DIR__.'/../../vendor/autoload.php';
use Zhcsam\Io\Multi\Worker;
$host = "tcp://0.0.0.0:9000";
$server = new Worker($host);
$server->onConnect = function($socket, $client){
echo "有一个连接进来了\n";
};
// 接收和处理信息
$server->onReceive = function($socket, $client, $data){
echo "给连接发送信息2222\n";
sleep(3);
$socket->send($client, "hello world cli8888ent \n");
// fwrite($client, "server hellow");
};
$server->start();
客户端的代码部分:
<?php
$host ="tcp://0.0.0.0:9000";
$cliect = stream_socket_client($host);
$new =time();
echo "客户端连接中\n";
fwrite($cliect,'客户端发来信息');
var_dump(fread($cliect, 65535));
echo "其它业务\n";
echo "用时".(time()-$new)."\n";
先来运行服务端php server.php 这时进入监听
运行客户端
php click.php
客户端连接中
string(126) "HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Connection: keep-alive
Content-length: 24
hello world cli8888ent
"
其它业务
用时3
再来看服务端的变化
php server.php
==== >>>> : new socket
resource(11) of type (stream)
==== >>>> : sendMessage
resource(12) of type (stream)
给连接发送信息2222
发送信息中。。。。==== >>>> : sendMessage
resource(12) of type (stream)
IO多路复用主要有select、poll、epoll三种模式,select/poll相差不大,主要是通过轮询来不断的检测是否有描述符已就绪,select默认情况下支持最多监控1024个描述符,poll则没有这个限制(底层通过链表实现,可动态增加);epoll不是通过轮询,而是通过回调(callback)方式主动通知已有描述符已就绪,相比较select/poll效率有明显提升。
不管哪种模式的IO多路复用,还都是同步IO,比如当监控的所有的描述符都还没有准备就绪,那么该进程还是阻塞在此