swoole算是nodejs在php中的一种实现,异步响应请求,性能超强

1 安装准备

1.1 安装swoole前必须保证系统已经安装了下列软件

php-5.3.10 或更高版本

gcc-4.4 或更高版本

make

autoconf

pcre (centos系统可以执行命令:yum install pcre-devel)

1.2 下载并解压

下载地址 https://github.com/swoole/swoole-src/releases

进入页面后选择download链接下的tar.gz的压缩包

下载源代码包后,解压

tar xzvf xxx.tar.gz

在终端进入源码目录,执行下面的命令进行编译和安装

cd swoole
phpize
./configure --enable-swoole-debug
make
sudo make install

编译参数根据自己的需求选择,详情参看官方文档。

1.3 编译安装成功后,修改php.ini

在php.ini中加入 extension=swoole.so

通过在命令行使用 php-m查看,是否安装了swoole

注意:如通重新编译的话需要 make clean

2 构建Swoole基本实例

2.1 tcp服务器实例

(来自w3cschool教程https://www.w3cschool.cn/swoole/bnte1qcd.html)

服务端代码:Server.php

<?php
// Server
class Server
{
private $serv; public function __construct() {
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 8,
'daemonize' => false,
'max_request' => 10000,
'dispatch_mode' => 2,
'debug_mode'=> 1
)); $this->serv->on('Start', array($this, 'onStart'));
$this->serv->on('Connect', array($this, 'onConnect'));
$this->serv->on('Receive', array($this, 'onReceive'));
$this->serv->on('Close', array($this, 'onClose')); $this->serv->start();
} public function onStart( $serv ) {
echo "Start\n";
} public function onConnect( $serv, $fd, $from_id ) {
$serv->send( $fd, "Hello {$fd}!" );
} public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
echo "Get Message From Client {$fd}:{$data}\n";
} public function onClose( $serv, $fd, $from_id ) {
echo "Client {$fd} close connection\n";
}
}
// 启动服务器
$server = new Server();

从代码中可以看出,创建一个swoole_server基本分三步:

  1. 通过构造函数创建swoole_server对象
  2. 调用set函数设置swoole_server的相关配置选项
  3. 调用on函数设置相关回调函数 关于set配置选项以及on回调函数的具体说明,请参考我整理的swoole文档( 配置选项)

    这里只给出简单介绍。onStart回调在server运行前被调用,onConnect在有新客户端连接过来时被调用,onReceive函数在有数据发送到server时被调用,onClose在有客户端断开连接时被调用。 这里就可以大概看出如何使用swoole:在onConnect处监听新的连接;在onReceive处接收数据并处理,然后可以调用send函数将处理结果发送出去;在onClose处处理客户端下线的事件。

客户端的代码:Client.php

<?php
class Client
{
private $client; public function __construct() {
$this->client = new swoole_client(SWOOLE_SOCK_TCP);
} public function connect() {
if( !$this->client->connect("127.0.0.1", 9501 , 1) ) {
echo "Error: {$fp->errMsg}[{$fp->errCode}]\n";
}
$message = $this->client->recv();
echo "Get Message From Server:{$message}\n"; fwrite(STDOUT, "请输入消息:");
$msg = trim(fgets(STDIN));
$this->client->send( $msg );
}
} $client = new Client();
$client->connect();

这里,通过swoole_client创建一个基于TCP的客户端实例,并调用connect函数向指定的IP及端口发起连接请求。随后即可通过recv()和send()两个函数来接收和发送请求。需要注意的是,这里我使用了默认的同步阻塞客户端,因此recv和send操作都会产生网络阻塞。

使用方法

进入到文件目录,在窗口1先启动php Serve.php,然后再开一个窗口(窗口2)启动php Client.php

窗口1内容:

# root @ WENGINE in /data/learnSwoole [9:24:57] C:130
$ php Server.php
Start
Get Message From Client 1:ceshi1
Client 1 close connection

窗口2内容:

# root @ WENGINE in /data/learnSwoole [9:23:07]
$ php Client.php
Get Message From Server:Hello 1!
请输入消息:ceshi1

2.2 web服务器

服务端代码 http_server.php

$http = new swoole_http_server("0.0.0.0", 9501);

$http->on('request', function ($request, $response) {
var_dump($request->get, $request->post);
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
}); $http->start();

Http服务器只需要关注请求响应即可,所以只需要监听一个onRequest事件。当有新的Http请求进入就会触发此事件。事件回调函数有2个参数,一个是$request对象,包含了请求的相关信息,如GET/POST请求的数据。

另外一个是response对象,对request的响应可以通过操作response对象来完成。$response->end()方法表示输出一段HTML内容,并结束此请求。

● 0.0.0.0 表示监听所有IP地址,一台服务器可能同时有多个IP,如127.0.0.1本地回环IP、192.168.1.100局域网IP、210.127.20.2 外网IP,这里也可以单独指定监听一个IP

● 9501 监听的端口,如果被占用程序会抛出致命错误,中断执行。

2.3 WebSocket服务器

服务端程序代码 ws_server.php

//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502); //监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
var_dump($request->fd, $request->get, $request->server);
$ws->push($request->fd, "hello, welcome\n");
}); //监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
echo "Message: {$frame->data}\n";
$ws->push($frame->fd, "server: {$frame->data}");
}); //监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
}); $ws->start();

WebSocket服务器是建立在Http服务器之上的长连接服务器,客户端首先会发送一个Http的请求与服务器进行握手。握手成功后会触发onOpen事件,表示连接已就绪,onOpen函数中可以得到$request对象,包含了Http握手的相关信息,如GET参数、Cookie、Http头信息等。

建立连接后客户端与服务器端就可以双向通信了。

● 客户端向服务器端发送信息时,服务器端触发onMessage事件回调

● 服务器端可以调用$server->push()向某个客户端(使用$fd标识符)发送消息

● 服务器端可以设置onHandShake事件回调来手工处理WebSocket握手

运行程序

客户端的代码

可以使用Chrome浏览器进行测试,JS代码为:

var wsServer = 'ws://127.0.0.1:9502';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) {
console.log("Connected to WebSocket server.");
}; websocket.onclose = function (evt) {
console.log("Disconnected");
}; websocket.onmessage = function (evt) {
console.log('Retrieved data from server: ' + evt.data);
}; websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
};
  • 不能直接使用swoole_client与websocket服务器通信,swoole_client是TCP客户端
  • 必须实现WebSocket协议才能和WebSocket服务器通信,可以使用swoole/framework提供的PHP WebSocket客户端
  • WebSocket服务器除了提供WebSocket功能之外,实际上也可以处理Http长连接。只需要增加onRequest事件监听即可实现Comet方案Http长轮询。

关于onRequest回调

swoole_websocket_server 继承自 swoole_http_server

  • 设置了onRequest回调,websocket服务器也可以同时作为http服务器
  • 未设置onRequest回调,websocket服务器收到http请求后会返回http 400错误页面
  • 如果想通过接收http触发所有websocket的推送,需要注意作用域的问题,面向过程请使用“global”对swoole_websocket_server进行引用,面向对象可以把swoole_websocket_server设置成一个成员属性

可以创建更多的服务器 参照官方文档尝试

https://wiki.swoole.com/wiki/page/475.html

3 使用laravel5.5实现的前后台通信实例

主要思路是使用php artisan 自建命令控制服务端,使用HTML5的websocket实现客户端功能

服务端:app/Console/Commands/Websocket.php内容

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use swoole_http_request;
use swoole_http_response;
use swoole_websocket_server; class WebSocket extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'websocket
{cmd=start : can use start|stop|status|restart}
{--daemon : set to run in daemonize mode}
'; /**
* The console command description.
*
* @var string
*/
protected $description = 'swoole server control'; /**
* server
*
* @var swoole_websocket_server
*/
private $server; /**
* * TYPE_ADMIN
* */
const TYPE_ADMIN = 0X00; /**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
} /**
* 处理不同command信息
*
* @return mixed
*/
public function handle()
{
$command = $this->argument('cmd');
$option = $this->option('daemon'); switch ($command) {
case 'start':
$this->initWs($option);
break;
case 'stop':
$res = $this->sendAdminRequest($command);
if ($res){
$this->info('stop the server successfully');
} else {
$this->info('the server is not running');
}
break;
case 'status':
$res = $this->sendAdminRequest($command);
if ($res){
$this->info($res);
} else {
$this->info('the server is not running');
}
break;
case 'restart':
$res = $this->sendAdminRequest($command);
if ($res){
$this->info('restart the server successfully');
} else {
$this->info('the server is not running');
}
break;
default:
$this->info('请按照下面格式输入命令:php artisan websocket {start|stop|status|restart}');
break;
} } //初始化服务端
public function initWs($daemonize = false)
{
if ($daemonize) {
$this->info('Starting Websocke server in daemon mode...');
} else {
$this->info('Starting Websocke server in interactive mode...');
} $server = new swoole_websocket_server('0.0.0.0', 9501);
$server->set([
'daemonize' => $daemonize,
'log_file' => '/var/log/websocket.log'
]); $server->on('close', function ($server, $fd) {
$this->info('close websocket server');
}); $server->on('open', function (swoole_websocket_server $server, $request) {
$this->info('websocket open');
}); $server->on('open', [$this, 'onOpen']);
$server->on('close', [$this, 'onClose']);
$server->on('message', [$this, 'onMessage']);
$server->on('request', [$this, 'onRequest']); $this->server = $server;
$this->server->start();
} public function onOpen(swoole_websocket_server $server, $request)
{
$this->info('websocket open');
} public function onClose($server, $fd)
{
$this->info('close websocket server');
} public function onMessage(swoole_websocket_server $server, $frame)
{
$this->info($frame->data);
$data = json_decode($frame->data, true); //对data进行逻辑处理
$reply = '发送的信息是:' . $data['message'];
$response = [
'status' => true,
'data' => $reply
];
$server->push($frame->fd, json_encode($response));
} //websocket客户端同样支持http协议
public function onRequest(swoole_http_request $request, swoole_http_response $response)
{
if ($request->post['type'] == self::TYPE_ADMIN) {
$ret = json_encode($this->commandHandle($request->post['content']));
return $response->end($ret);
}
} //操作命名
public function commandHandle($command) {
if ($command == 'status') {
$this->info('handle status');
return $this->server->stats();
}
if ($command == 'restart') {
$this->info('handle restart');
return $this->server->reload();
}
if ($command == 'stop') {
$this->info('handle stop');
return $this->server->stop() && $this->server->shutdown();
}
return 'Unknown Command';
} //发送http请求
public function sendAdminRequest($content) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:9501");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Expect:']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
'type' => self::TYPE_ADMIN,
'content' => $content
]); $response = curl_exec($ch);
curl_close($ch);
return $response;
} }

客户端内容

<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>websocket client</title>
</head>
<body>
<div>
<input id="message-content" type="text" name="message" />
<button onclick="sendMessage()">发送消息</button>
</div>
</body>
<script>
var wsServer = 'ws://115.159.81.46:9501';
var websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) {
console.log("Connected to WebSocket server.");
}; websocket.onclose = function (evt) {
console.log("Disconnected");
}; websocket.onmessage = function (evt) {
console.log('从服务器接收到json信息: ' + evt.data);
alert('服务器返回信息:' + JSON.parse(evt.data).data);
}; websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
}; function sendMessage(){
var content = document.getElementById('message-content').value;
var data = {
message : content,
}
websocket.send(JSON.stringify(data));
};
</script>
</html>

启动websocket服务器

进入系统根目录,

php artisan websocket [--daemon] //是否使用daemon模式

php artisan websocket start|stop|status|restart //默认是start

swoole创建websocket服务器的更多相关文章

  1. swoole 创建tcp服务器

    server.php <?php /** * 创建tcp服务器 * Date: 2019/1/15 */ $serv = new swoole_server('127.0.0.1', 9501) ...

  2. swoole 创建UDP服务器

    udp_server.php <?php // 创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP $serv = new swoole_serv ...

  3. swoole 创建web服务器

    http_server.php $http = new swoole_http_server("0.0.0.0", 9501); // 请求监听事件 $http->on('r ...

  4. Swoole学习(五)Swoole之简单WebSocket服务器的创建

    环境:Centos6.4,PHP环境:PHP7 服务端代码 <?php //创建websocket服务器 $host = '0.0.0.0'; $port = ; $ws = new swool ...

  5. 04.swoole学习笔记--webSocket服务器

    <?php //创建webSocket服务器 $serv=); //获取请求 //on //open 建立连接 $serv:服务器 $request:客户端信息 $serv->on('op ...

  6. php swoole 和 websocket的初次碰撞

    php swoole 扩展仿佛为php开发打开了一扇窗户 阅读文档 https://wiki.swoole.com php workman和swoole原来是两个东东 swoole的使用范围更广,能做 ...

  7. Swoole练习 websocket

    WEBSOCKET 服务端代码 //创建websocket服务器对象,监听0.0.0.0:9502端口 $ws = new swoole_websocket_server("0.0.0.0& ...

  8. 【Swoole】简单安装与创建TCP服务器

    pecl install swoole PHP的异步.并行.高性能网络通信引擎,使用纯C语言编写,提供了php语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据 ...

  9. Swoole学习(三)Swoole之UDP服务器的创建

    环境:Centos6.4,PHP环境:PHP7 <?php //创建UCP服务器(UDP服务器相对于TCP服务器通信更可靠些) /** * $host 是swoole需要监听的ip,如果要监听本 ...

随机推荐

  1. manjaro安装后你需要做的配置

    1.切换中国源 sudo gedit /etc/pacman-mirrors.conf 如果提示没有gedit , 则执行命令 : sudo pacman -S gedit 修改如下地方为中国: On ...

  2. js call(),apply(),对象冒充,改变变量作用域

    1.apply(); function box(n1,n2){ return n1+n2; } function pox(n1,n2){ alert(box.apply(this,[n1,n2])); ...

  3. 屏蔽firefox浏览器连接失败页面的广告

    现象 最近一直在使用firefox浏览器(版本:57.0.1(64位)),同步书签特别方便,但是最近发现当访问的一个不存在的网址时,连接失败页面竟然有广告!firefox不是号称没有广告吗? 分析 F ...

  4. 【PHP 模板引擎】Prototype 原型版发布!

    在文章的开头,首先要向一直关注我的人说声抱歉!因为原本是打算在前端框架5.0发布之后,就立马完成 PHP 模板引擎的初版.但我没能做到,而且一直拖到了15年元旦才完成,有很严重的拖延症我很惭愧,再次抱 ...

  5. 1012: A MST Problem

    1012: A MST Problem 时间限制: 1 Sec  内存限制: 32 MB提交: 63  解决: 33[提交][状态][讨论版][命题人:外部导入] 题目描述 It is just a ...

  6. python 删除空白

    Python能够找出字符串开头和末尾多余的空白.要确保字符串末尾没有空白,可使用方法rstrip() . >>> favorite_language = 'python ' > ...

  7. C# 声明bool变量

    与现实世界不同,在编程的世界中,每一件事情要么黑,要么白:要么对,要么错:要么是真的,要么是假的.例如,假定你创建一个名为x的整数变量,把值99赋给x,然后问:“x中包含了值99吗?”答案显然是肯定的 ...

  8. java 基础词汇 必须 第九天

    Collection 集合 List 列表集合 Set 不重复集合 Linked 链表 Vector 线程安全集合 Hash 哈希值 tree 树型结构 Map 键值对集合 add 增加 remove ...

  9. SHGetSpecialFolderLocation获取开始文件夹

    SHGetSpecialFolderLocation函数可以获取windows 特殊目录 函数原型:(MSDN官方链接:https://msdn.microsoft.com/en-us/library ...

  10. mysql 自增主键为什么不是连续的?

    由于自增主键可以让主键索引尽量地保持递增顺序插入,避免了页分裂,因此索引更紧凑 MyISAM 引擎的自增值保存在数据文件中 nnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 ...