選択が基づいているかのように、いくつかのものを選択過程でいくつかの契約、およびいくつかのスレッド選択プロセスを対処するためのシステムのプラットフォームと言語のプログラミングを決定します。次はネットワークIOモデルであるかを理解する必要がありますか?
QQや他のクライアント側のシステムは、ソケットを介して通信し、Linuxカーネルに実装され、どのようにそのソケットを作成するには?
PHPは(二つの機能は、それぞれstream_socket_server、ソケットのソケットを作成することができる持っている)とsocket_create()、これら2つの関数は、ソケットを作成するためには、ほとんど差があるstream_socket_serverラッパー関数であり、socket_createはネイティブです。唯一の経験stream_socket_server機能。
次は、使用するネイティブ(swooleがパッケージ化されたサービスを、作成swooleする相対的な)サービスを作成するには、この機能stream_socket_serverをした後、接続してサービスを要求します。
ネイティブ初期の経験を実装するコード:
$ホスト= "TCP://0.0.0.0:9501";
// ソケットサービスを作成します。
$サーバー= stream_socket_server($ホスト)。
エコー$ホスト "\ n" は。
// クライアントとの接続を確立
// オープンサービスの後、サービスが一時停止状態で、接続が入って来て、接続を作成するために待っています
// stream_socket_acceptがブロックされています
// 来るのに長い時間が接続されていないため、それが異常を警告接続タイムアウトを報告します、が先行異常を抑制することができます@
$クライアント= @stream_socket_accept($サーバー)。
var_dump($クライアント)。
$ホスト= "TCP://0.0.0.0:9501";
//ソケットサービスを作成します。
$サーバー= stream_socket_server($ホスト)。
エコー$ホスト "\ n" は。
//クライアントとの接続を確立
//オープンサービスの後、サービスは接続がで来て、接続を作成するのを待つ、中断状態にあります
// stream_socket_acceptがブロックされています
//何も接続が長い時間に来ない、それは、接続タイムアウト警告異常を報告しますが先行し、異常を抑制することができます@
// 接続がオーバーに来て、接続をリッスンを繰り返しながら、それは無限ループによって達成されたときに接続を待ちます
一方、(TRUE){
$クライアント= @stream_socket_accept($サーバー)。
var_dump($クライアント)。
}
サービスは、ときに、クライアントの接続先、サーバーがリスニングを続行するために接続するための接続を待機した後、受信するように接続され、オンされた後、データを長時間受信されない場合、クライアントは、リターン情報サーバーのサーバーを待っています、それは壊れてリセットされます。
カール(56)のrecv失敗:ピアによって接続がリセット
サーバから返された情報を受信しませんフロントの後ろに接続された着信接続は、複数のスクイズに接続されているとき
$ホスト= "TCP://0.0.0.0:9501";
//ソケットサービスを作成します。
$サーバー= stream_socket_server($ホスト)。
エコー$ホスト "\ n" は。
//クライアントとの接続を確立
//オープンサービスの後、サービスは接続がで来て、接続を作成するのを待つ、中断状態にあります
// stream_socket_acceptがブロックされています
//何も接続が長い時間に来ない、それは、接続タイムアウト警告異常を報告しますが先行し、異常を抑制することができます@
接続がオーバーに来て、接続をリッスンを繰り返しながら、それは無限ループによって達成されたときに//接続を待ちます
一方、(TRUE){
//クライアントとの接続を確立
$クライアント= @stream_socket_accept($サーバー)。
// クライアントの情報を読み取ります
$データ= @fread($クライアント、1024);
// クライアントにメッセージを送信
@fwrite($クライアント、 'ABC');
// 接続を閉じます
@fclose($クライアント)。
var_dump($クライアント)。
}
ここでは、サーバがクライアントに、クライアントの情報と送信情報を読み取りです
クライアントの作成:
$host = "tcp://127.0.0.1:9501";
//创建一个客户端的连接,与服务端accept进行信息交互
$client = stream_socket_client($host);
//给socket通道发送信息
fwrite($client,"hello world");
//接收读取socket通道发送来的信息
var_dump(fread($client,65535));
//关闭连接
fclose($client);
在tcp创建连接之后,假设需要做个耗时操作,sleep(3)一下,看看多个客户端同时连接进来会发生什么情况?
服务端:
$host = "tcp://0.0.0.0:9501";
//创建socket服务
$server = stream_socket_server($host);
echo $host."\n";
//建立与客户端的连接
//开启服务之后,服务就处于一个挂起的状态,等待连接进来然后创建连接
//stream_socket_accept是阻塞的
//长时间没有连接进来,就会报出一个连接超时警告异常,可以在前面加@来抑制异常
//监听连接,当一个连接进来之后,就结束了,那通过一个死循环while来实现一直重复监听连接
while(true){
//建立与客户端的连接
$client = @stream_socket_accept($server);
sleep(3);
//读取客户端的信息
$data = @fread($client,1024);
//发送信息给客户端
@fwrite($client,'abc');
//关闭连接
@fclose($client);
var_dump($data);
}
客户端:
$host = "tcp://127.0.0.1:9501";
//创建一个客户端的连接,与服务端accept进行信息交互
$client = stream_socket_client($host);
$time = time();
//给socket通道发送信息
fwrite($client,"hello world");
//接收读取socket通道发送来的信息
var_dump(fread($client,65535));
//关闭连接
fclose($client);
echo "\n".time()-$time;
这个时候发现第2个客户端必须等待第1个客户端完成之后才会连接,这是为什么呢?
这主要是 stream_socket_server是单线程处理连接的,必须等待前面的连接处理之后再处理后面的,这种状态叫做阻塞调用。那如何解决呢?
swoole 通过Reactor模式解决这个问题,php-fpm通过多进程模式解决这个问题,这些模式统称为网络IO模型。
网络IO模型就好比 socket连接处理的框架或架构,比如:原生PHP=》laravel,thinkphp,yii。
IO管理连接模型
内核:可以理解为 帮助我们去处理这份代码,如何去读,写,关闭,输出。
用户空间:可以理解为去告诉内核处理一些事情,代码就在用户空间里写。
5大io模型:
- 阻塞I/O(blocking IO):用户空间一直等待内核处理完之前未处理完的事情,且在等待时间内不会去做其他的事情。效率是最低的。
- 非阻塞I/O(noblocking IO):用户空间先检查内核是否有未处理完的事情,若有,就去做其他的事情,且每隔一段时间检查一次内核是否处理完,这种就是轮询的方式。
- I/O多路复用 (IO multiplexing ):开辟多个内核,用户空间根据哪个内核空闲就将信息发送给该内核去处理。
- 信号驱动I/O (signal driven IO):用户空间设置一个信息号就会去做其他的事情(对用户空间来说是非阻塞的),内核处理完未处理的事情之后会处理这个事情,处理完这个事情之后会返回该信号,表示处理完这个事情。触发回调函数。
- 异步I/O (asynchronous IO):预定模式 ,用户空间一般用事件event=》事先今天发送一个信息给内核,并告诉内核在指定的时间节点去处理这件事情,(对于用户空间来说是非阻塞的),触发回调函数。通常事件发送完之后还需要事件清理(原因是内核处理完一件事情后,在下一个时间节点有可能在做其他的事情了)。
为什么要理解5大IO模型?对我们来说有什么意义?
通过理解网络编程的一些技术知识,为了更好的理解学习swoole。
应用composer命名空间
io
src
SignalDriven
worker.php
test
signal-driver
server.php
client.php
先创建io目录,打开cmd,cd到io目录下,输入composer init,回车,创建属于自己的命名空间;
配置一下Package name ,Auth,Package Type,License
就会在io目录下生成一个composer.json文件
然后创建一下目录 ,src(存放自己原生写的封装的类),test目录(测试用的目录包)。然后再composer.json文件里配置一下命名空间,添加配置如下:
"autoload":{
"psr-4":{
"zjl\\io\\":"./src/"
}
},
测试一下:
在src目录下创建Index.php,编写测试代码如下:
然后composer update一下
这时候会生成vendor目录及文件
然后在io目录下创建一个test.php。(可以参照workerman的架构)
然后再cmd 上运行一下 php test.php ,这样就可以了。
Worker.php
<?php
namespace zjl\io\Blocking;
/**
* 用户连接服务
*/
class Worker
{
/**
* 自定义服务的事件注册函数
* 闭包函数
* @var null
*/
public $onReceive = null;
public $onConnect = null;
public $onClose = null;
//连接
public $socket = null;
public function __construct($socket_address)
{
$this->socket = stream_socket_server($socket_address);
echo $socket_address."\n";
}
/**
* 处理事情
*/
public function accept()
{
//接收连接,处理事情
while(true)
{
$client = @stream_socket_accept($this->socket);
//判断是不是闭包
if(is_callable($this->onConnect)){
//执行闭包函数
($this->onConnect)($this,$client);
}
$data = fread($client,65535);
if(is_callable($this->onReceive)){
//执行闭包函数
($this->onReceive)($this,$client,$data);
}
//处理完关闭连接
fclose($client);
}
}
public function send($conn,$data)
{
fwrite($conn,$data);
}
//启动服务
public function start()
{
$this->accept();
}
}
server.php
<?php
require __DIR__."/../../vendor/autoload.php";
use zjl\io\Blocking\Worker;
$host = "tcp://0.0.0.0:9501";
$server = new Worker($host);
//连接服务
$server->onConnect = function ($server,$client){
echo "有一个连接进来\n";
var_dump($client);
};
//接收和处理信息
$server->onReceive = function ($server,$client,$data){
echo "给连接发送信息\n";
$server->send($client,"hello world client \n");
};
$server->start();
测试
php server.php