AI编程生活评测

ThinkPHP 5.1下使用PHPSocket.IO实现websocket通讯

编程笔记 / 2018-07-04 / 5 min
PHPSocket.IO 的官方介绍:
PHPSocket.IO是PHP版本的Socket.IO服务端实现,基于workerman开发,用于替换node.js版本Socket.IO服务端。PHPSocket.IO底层采用websocket协议通讯,如果客户端不支持websocket协议, 则会自动采用http长轮询的方式通讯。PHPSocket.IO实现的Polling通信机制包括Adobe Flash Socket、AJAX长轮询、JSONP轮询等。具体采用哪种机制通讯对于开发者完全透明, 开发者使用的是统一的接口。

项目 github 地址https://github.com/hsu1943/thinksocketio,项目已经做了很多更新,请按照项目 readme 使用。

下面我们使用 ThinkPHP5.1 框架来整合 PHPSocket.IO 实现客户端和服务端的通讯。 使用的本地开发环境是PHP5.6.30 + Apache2.4.25

创建新项目

// 使用ThinkPHP5.1新建项目thinksocket;
$ composer create-project topthink/think thinksocket 5.1.*;
// 进入项目,下载workerman/phpsocket.i包;
$ cd thinksocket
$ composer require workerman/phpsocket.io
这样你的安装工作就做好了。接下来配置需要的模块。

配置模块

结合前阵子的文章:ThinkPHP 5.1自动生成模块及目录、文件 我们编辑好 build.php 文件:
return [
    // 生成应用公共文件
    '__file__' => ['common.php'],
    // 定义socketio模块的自动生成
    'socketio'     => [
        '__file__'   => ['common.php'],
        '__dir__'    => ['controller', 'model', 'view'],
        'controller' => ['Index', 'Server'],
        'model'      => [],
        'view'       => ['index/index'],
    ],
    // 其他更多的模块定义
];
然后运行
$ php think build
Successed
这样就有了socketio模块下面 controllerIndex.phpServer.php 两个控制器和 view 下一个 index/index 一个视图。

创建服务端

编辑控制器 Server.php 为(具体参考注释):
<?php
namespace appsocketiocontroller;

use WorkermanWorker;
use PHPSocketIOSocketIO;
use thinkDb;

class Server{

    public function index(){
        // 在2021端口创建服务
        $io = new SocketIO(2021);
        $io->on('connection', function($socket)use($io){
            $socket->on('chat message', function($msg)use($io){
                $io->emit('chat message', $msg);
            });
            // 监听到新的客户端连接即在服务端输出'new connection'
            echo 'new connection'."n";
            // 并向服务端发送'连接成功'
            $socket->emit('success', '连接成功');
            // 服务端发送消息过来
            $socket->on('sendMsg', function($msg)use($io){
                // 在服务端输出消息
                echo $msg."n";
                // 在收到的消息前面拼接'收到'后向客户端发送回去
                $io->emit('sendMsg', '收到"'.$msg.'"');
                // 将接受到的消息存储到数据库
                $data['msg'] = $msg;
                Db::table('msg')->insert($data);
            });
        });
        // 启动服务
        Worker::runAll();
    }
    // 测试数据库链接
    public function ceshi(){
        $msg = Db::table('msg')->select();
        var_dump($msg);
    }
}
为了测试,在数据库新建了一张表 msg 用来存储客户端发送过来的消息。
CREATE TABLE `msg` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `msg` varchar(250) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;
这样一个可以返回连接成功消息、接收消息、回复消息、存储消息的简单服务端就做好了。

创建客户端

我们在 Index.php 控制器中这样写:
<?php
namespace appsocketiocontroller;

use thinkController;

class Index extends Controller
{
    public function index()
    {
        return view();
    }
}
在对应的模版文件 view/index/index.html 中代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>测试WebSocket</title>
</head>
<body>

<input type="text" name="" id="msg">
<button id='send'>发送消息</button>

</body>
<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
// 如果服务端不在本机,请把127.0.0.1改成服务端ip
var socket = io('http://127.0.0.1:2021');
// 当连接服务端成功时触发connect默认事件
socket.on('connect', function(){
    console.log('connect success');
});
socket.on('success', function(msg){
    // 连接后弹出服务端返回的消息'连接成功'
    alert(msg);
});

socket.on('sendMsg', function(msg){
    // 将服务端返回的消息输出到控制台
    console.log(msg);
});

// 向服务端发送消息
$('#send').on('click', function(){
    var text = $('#msg').val();
    socket.emit('sendMsg', text);
    // alert(text);
})
</script>
</html>
到这里,一个具备连接服务端,发送消息,接受并输出消息到控制台的简单 websocket 客户端就建好了。

为服务端绑定入口文件

在项目的 public 文件夹下新建一个入口文件 server.php 并将其绑定到 socketio 模块的 Server 控制器 index 方法; 入口文件 server.php 代码:
<?php
// [ 应用入口文件 ]
namespace think;

// 加载基础文件
require __DIR__ . '/../thinkphp/base.php';

// 执行应用并响应(绑定)
Container::get('app')->bind('socketio/Server')->run()->send();

测试通讯

运行服务端 直接运行项目 public 目录下的 server.php
D:xampphtdocsthinksocketpublic>php server.php
----------------------- WORKERMAN -----------------------------
Workerman version:3.5.11          PHP version:5.6.30
------------------------ WORKERS -------------------------------
worker               listen                              processes status
PHPSocketIO          socketIO://0.0.0.0:2021             1         [ok]
ThinkPHP 5.1下使用PHPSocket.IO实现websocket通讯 这个输出表示 PHPSocketIO 已经成功在 2021 端口运行监听中。 我们打开浏览器打开 http://localhost/thinksocket/public/index.php/socketio/ 页面; 可以看到页面会弹出‘链接成功’,同时可以看到服务端 cmd 终端上打印出 new connection ,同事浏览器控制台会输出 connect success ,代表已经成功与服务端连接上。 测试发送消息 在页面输入框内输入任意信息,服务端 cmd 终端将会打印信息,代表服务端收到信息,然后服务端控制台将会输出‘收到’+发送的信息。 至此,我们的测试完成。 温馨提示:对程序修改后需要重新启动服务端才能生效 以上只是简单的利用 ThinkPHP5.1 的框架测试 PHPSocket.IO 通讯。 源代码地址:https://github.com/hsu1943/thinksocketio 供大家参考。

update (代码以Github为准)

  • 2019/01/22: Github 项目代码已经更新,加入了用户唯一表示记录以及向指定用户推送消息。详情看:ThinkPHP 5.1+PHPSocket.IO实现websocket搭建聊天室+私聊
  • 2019-06-19 增加在线人数统计,在线用户列表,修改昵称,添加系统主动推送接口(广播或私信);
  1. maomao 2019-07-02 · 16:07

    php server.php 执行了,没有workman的信息,也没有报错

    1. 行星带 2019-07-03 · 09:57

      下载测试的是最新的源码吗?项目地址,https://github.com/hsu1943/thinksocketio,根据readme
      操作即可

  2. 嗯啊 2019-01-22 · 23:04

    服务器主动给客户端推送消息的案例有么,亲

    1. 行星带 2019-01-23 · 10:02

      1月22号的代码大更新,加入向指定客户端推送消息的功能,看github上项目代码里有注释的,下一步是更新一对一私聊。

      1. 嗯啊 2019-01-23 · 19:30

        感谢回复,我想实现个服务端主动给已连接的用户推送消息,类似于推送。

        1. 行星带 2019-01-24 · 11:20

          新更新的代码包括了一对一推送,只需要在这基础上与使用系统用户表对应的用户就可以向指定用户推送消息了。

        2. 行星带 2019-06-21 · 17:07

          最新更新了服务端主动向用户推送消息。

  3. y 2019-01-10 · 19:35

    你好,还有关于tp5整合phpsocket.io的更多资料吗?网上好难找到。。。

    1. 行星带 2019-01-11 · 10:02

      更多的应用可以参考引入的三方包里的chat案例位置:vendor\workerman\phpsocket.io\examples

  4. y 2019-01-09 · 14:34

    为什么数据库那边更新不了数据呢?

    1. 行星带 2019-01-09 · 14:44

      确保你成功连上数据库了,并创建了文中所提到的保存信息的表。

      1. y 2019-01-09 · 16:24

        嗯嗯,已经解决了,database.php有两个,之前没看到socketio里面的,只看到config中的。还有就是这个只能发送一次的问题解决了吗?

        1. 行星带 2019-01-09 · 17:44

          能连上数据库的情况下,你向服务器发送的消息会存入数据表内,没出现过只能发一次的情况,修复数据库链接的问题后,你需要重启服务端。看文末的温馨提示。

          1. y 2019-01-09 · 17:59

            我的意思是在页面中点击发送能发送出去,但是再点击就没用了,只能刷新页面再发才能

          2. 行星带 2019-01-09 · 18:53

            你测试一下你的数据库链接正常不?能正常写入数据吗?也可以把数据库写入数据那部分注释掉。

          3. y 2019-01-09 · 22:06

            能正常显示写入到数据库,问题主要就是一次连接只能发送一次,要再发送就只能再刷新页面再连接一次。

  5. ff 2019-01-09 · 11:18

    您好,为什么项目中的没有thinkphp文件夹呢?入口文件中的thinkphp/base.php找不到

  6. ff 2019-01-09 · 11:17

    您好,为什么项目中没有thinkphp文件夹呢?入口文件中的thinkphp文件中的base.php找不到

    1. 行星带 2019-01-09 · 11:19

      你需要在运行一下composer install安装依赖包

      1. ff 2019-01-09 · 11:22

        是在项目的目录cmd执行 composer install 吗?

      2. ff 2019-01-09 · 11:41

        抱歉问一下,在哪运行composer install啊?

      3. ff 2019-01-09 · 11:52

        您好,这边运行了,但是结果好像不如意,好多下载失败。

        1. 行星带 2019-06-21 · 17:08

          记得给composer换个镜像

  7. 360060316 2018-11-11 · 22:12

    只能发送一次信息 第二次就不行了是怎么回事

    1. 行星带 2018-11-12 · 09:11

      看下报错信息是什么呢?如果没有报错,在后台打个日志调一下找到哪一步出问题了。

      1. 360060316 2018-11-12 · 09:56

        没有发现错误 握手成功发送一次消息,然后再发送服务器就收不到消息了

        1. 行星带 2018-11-13 · 11:58

          也就是只有链接成功的消息,没有收到主动发送的消息了对吧,检查消息有没有从客户端发出,消息标识’sendMsg’是否与后端接受消息的标识一致,将后端接受到的消息保存到日志(或数据库)中确认是否有消息,最后检查消息回显。

          1. 360060316 2018-11-13 · 11:59

            我找到问题了

          2. 行星带 2018-11-13 · 12:00

            分享一下问题和解决方案呗!

        2. ff 2019-01-09 · 14:33

          你好,你这个问题是怎么解决的呢?

  8. 小熊 2018-08-16 · 00:41

    你好,请教一下大神。我按照您的方法操作,运行 php server.php文件时,Class ‘think\Container’ not found in F:\phpStudy\PHPTutorial\WWW\tp5\public\server.php on line 9

    请教一下这个问题怎么结局呢? 在windows和centos环境下都是如此。

    1. 行星带 2018-08-17 · 23:53

      1. 确认你的ThinkPHP版本是不是5.1.*也就是5.1的最新版;
      2. 确认你的入口文件使用了”namespace think;”的命名空间;
      3. 检查”F:\phpStudy\PHPTutorial\WWW\tp5\thinkphp\library\think\Container.php”类文件是否存在;如果不存在是不是没有使用”composer install”安装框架核心;

      我能想到的就这么些,如果你已经找到问题了,可以回复一下。

  9. 王光卫博客 2018-07-12 · 22:51

    技术大牛,以后多来学习

    1. 行星带 2018-07-17 · 21:35

      我是一个地道的小白

  10. 海拔科技 2018-07-09 · 12:39

    站长,我已经添加过贵站的友联了,你这边也添加下吧?

    1. 行星带 2018-07-09 · 18:01

      嗯,已经添加了!