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. php的yii框架开发总结7

    protected\config\main.php是整个网站中很重要的一个文件,引用文件,连接数据库,默认页面等都是在这里设置: 'import'=>array( 'application.mo ...

  2. 正则表达式转换python2的print为python3风格

    直接查找 print ([^\n\(]*)替换为 print($1)

  3. windows如何关闭指定端口

    关闭windows中被占用的端口,比如我们常见的8080端口被占用了 1.查找端口的PID netstat -aon|findstr "8080" 如图 PID为3888 2.关闭 ...

  4. 快餐店之间插入仓库,路最短,DP,POJ(1485)

    题目链接:http://poj.org/problem?id=1485 暂时我还没想出思路求路径.哈哈哈,先写一下中间步骤吧. #include <stdio.h> #include &l ...

  5. 问题 C: P4 游戏中的Human角色

    题目描述 在一个平面打斗游戏中,任何的角色(Role)都有血量(blood)和位置loc(此处loc是Location类的实例)属性.有了Role类,可以派生出不同的角色,如人.神仙.怪兽等.如下程序 ...

  6. lintcode 77.Longest Common Subsequence(最长公共子序列)、79. Longest Common Substring(最长公共子串)

    Longest Common Subsequence最长公共子序列: 每个dp位置表示的是第i.j个字母的最长公共子序列 class Solution { public: int findLength ...

  7. Firefox 修改User Agent

    Android 版 Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML ...

  8. 【翻译】Emmet(Zen Coding)官方文档 之六 自定义 Emmet

    [说明]本系列博文是依据 Emmet 官方文档翻译的,原文地址为:http://docs.emmet.io/,部分内容已经在博主之前的博文中节选过,为方便已经收藏过之前博文的朋友,没有删除这些博文,仅 ...

  9. C#中datatable的用法/传数据

    在开发中,我们常用到表类型数据,不同于string,int....那么datatable类型如何定义呢,具体怎么使用呢,代码如下: namespace Common.Table { using Sys ...

  10. python备份mysql数据库

    介绍使用python结合mysqldump对mysql数据库进行备份 import os import sys import configparser import time def test_fil ...