workerman的使用-实现简单的聊天室
wokerman介绍
Workerman是一款纯PHP开发的开源高性能的PHP 应用容器。
Workerman不是重复造轮子,它不是一个MVC框架,而是一个更底层更通用的服务框架,你可以用它开发tcp代理、梯子代理、做游戏服务器、邮件服务器、ftp服务器、甚至开发一个php版本的redis、php版本的数据库、php版本的nginx、php版本的php-fpm等等。Workerman可以说是PHP领域的一次创新,让开发者彻底摆脱了PHP只能做WEB的束缚。
Workerman的一些应用方向如下:
1、即时通讯类
例如网页即时聊天、即时消息推送、微信小程序、手机app消息推送、PC软件消息推送等等
[示例 workerman-chat聊天室 、 web消息推送 、 小蝌蚪聊天室]
2、物联网类
例如Workerman与打印机通讯、与单片机通讯、智能手环、智能家居、共享单车等等。
[客户案例如 易联云、易泊时代等]
3、游戏服务器类
例如棋牌游戏、MMORPG游戏等等。[示例 browserquest-php]
4、HTTP服务
例如 写高性能HTTP接口、高性能网站。如果想要做HTTP相关的服务或者站点强烈推荐 webman
5、SOA服务化
利用Workerman将现有业务不同功能单元封装起来,以服务的形式对外提供统一的接口,达到系统松耦合、易维护、高可用、易伸缩。[示例 workerman-json-rpc、 workerman-thrift]
6、其它服务器软件
例如 GatewayWorker,PHPSocket.IO,http代理,sock5代理,分布式通讯组件,分布式变量共享组件,消息队列、DNS服务器、WebServer、CDN服务器、FTP服务器等等
7、组件
例如异步redis,异步http客户端,物联网mqtt客户端,消息队列 workerman/redis-queue 、 workerman/stomp、workerman/rabbitmq ,文件监控组件,还有很多第三方开发的组件框架等等
显然传统的mvc框架很难实现以上的功能,所以也就是workerman诞生的原因。
安装workerman(macOS)
WorkerMan实际上就是一个PHP代码包,如果你的PHP环境已经装好,只需要把WorkerMan源代码或者demo下载下来即可运行。
设置镜像
由于国内访问composer比较慢,建议设置阿里云composer镜像,运行如下命令设置阿里云代理
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
安装workerman
在一个空目录中运行
composer require workerman/workerman
开发实例
实例一、使用HTTP协议对外提供Web服务
我们使用编辑器打开workerman项目目录
创建http_test.php文件
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 创建一个Worker监听2345端口,使用http协议通讯
$http_worker = new Worker("http://0.0.0.0:2345");
// 启动4个进程对外提供服务
$http_worker->count = 4;
// 接收到浏览器发送的数据时回复hello world给浏览器
$http_worker->onMessage = function(TcpConnection $connection, $data)
{
// 向浏览器发送hello world
$connection->send('hello world');
};
Worker::runAll();
执行启动命令
php http_test.php start
在浏览器访问 127.0.0.1:2345 即可看到hello world
实例二、使用WebSocket协议对外提供服务
创建ws_test.php文件
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 注意:这里与上个例子不同,使用的是websocket协议
$ws_worker = new Worker("websocket://0.0.0.0:2000");
// 启动4个进程对外提供服务
$ws_worker->count = 4;
// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function(TcpConnection $connection, $data)
{
// 向客户端发送hello $data
$connection->send('hello ' . $data);
};
// 运行worker
Worker::runAll();
创建websocket.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="">
<input type="text" id="name" placeholder="请输入你的名字">
<input type="submit" id="submit">
</form>
</body>
<script src="//lib.sinaapp.com/js/jquery/3.1.0/jquery-3.1.0.slim.min.js"></script>
<script>
$(function () {
let ws = new WebSocket("ws://127.0.0.1:2000")
$("#submit").click(function () {
let name = $('#name').val()
ws.send(name)
// return false是防止submit提交后刷新页面
return false
})
ws.onmessage = function (e) {
alert(e.data)
}
})
</script>
</html>
运行
php ws_test.php start
浏览器打开websocket.html,发送你输入的值,你就可以收到服务端返回的数据
实例三、直接使用TCP传输数据
创建tcp_test.php
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 创建一个Worker监听2347端口,不使用任何应用层协议
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// 启动4个进程对外提供服务
$tcp_worker->count = 4;
// 当客户端发来数据时
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
// 向客户端发送hello $data
$connection->send('hello ' . $data);
};
// 运行worker
Worker::runAll();
命令行运行
php tcp_test.php start
测试:命令行运行(新开个终端窗口)
(以下是mac命令行效果,与windows下效果有所不同)
终端中输入
telnet 127.0.0.1 2347
输入 world
haoran>~ $ telnet 127.0.0.1 2347
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
world
hello world
就能收到服务端返回的数据
注意:如果提示command not found: telnet,需要安装telnet,命令如下:
brew install telnet
退出telnet
control + ]后输入quit
实现简单的聊天室
创建chat.php
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$global_uid = 0;
// 当客户端连上来时分配uid,并保存连接,并通知所有客户端
function handle_connection($connection)
{
global $ws_worker, $global_uid;
// 为这个连接分配一个uid
$connection->uid = ++$global_uid;
}
// 当客户端发送消息过来时,转发给所有人
function handle_message(TcpConnection $connection, $data)
{
global $ws_worker;
// 循环所有用户,将消息发送给他们
foreach($ws_worker->connections as $conn)
{
$conn->send("user[{$connection->uid}] said: $data");
}
}
// 当客户端断开时,广播给所有客户端
function handle_close($connection)
{
global $ws_worker;
foreach($ws_worker->connections as $conn)
{
$conn->send("user[{$connection->uid}] logout");
}
}
// 创建一个文本协议的Worker监听2347接口
$ws_worker = new Worker("websocket://0.0.0.0:2347");
// 只启动1个进程,这样方便客户端之间传输数据
$ws_worker->count = 1;
// 客户端连接
$ws_worker->onConnect = 'handle_connection';
// 客户端发送消息
$ws_worker->onMessage = 'handle_message';
// 客户端断开
$ws_worker->onClose = 'handle_close';
Worker::runAll();
新建一个vue项目,我们使用vue连接测试
// App.vue
<template>
<ul>
<li v-for="(mes, i) in message" :key="i">{{mes}}</li>
</ul>
<form @submit.prevent="onSubmit">
<input type="text" v-model="content">
<input type="submit" >
</form>
</template>
<script setup>
import { ref, reactive } from 'vue'
const message = reactive(['hello', '你好'])
let content = ref()
const ws = new WebSocket('ws://127.0.0.1:2347')
ws.onopen = function() {
alert('连接成功')
}
ws.onmessage = function(e) {
console.log(e);
message.push(e.data)
}
const onSubmit = () => {
ws.send(content.value)
}
</script>