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-rpcworkerman-thrift]

6、其它服务器软件
例如 GatewayWorkerPHPSocket.IOhttp代理sock5代理分布式通讯组件分布式变量共享组件消息队列、DNS服务器、WebServer、CDN服务器、FTP服务器等等

7、组件
例如异步redis异步http客户端物联网mqtt客户端,消息队列 workerman/redis-queueworkerman/stompworkerman/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

image-20221022200639335

输入 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>

posted @ 2022-10-22 20:26  d浩然  阅读(511)  评论(0编辑  收藏  举报