PHP 怎么PHP 双人操作

wen PHP项目 2

本文目录导读:

PHP 怎么PHP 双人操作

  1. 最基础方案:轮询 + 数据库(适合学习/小规模)
  2. 推荐方案:WebSocket + Ratchet(实时双向)
  3. 文件锁方案(最简单的双人文件编辑)
  4. 推荐的生产级方案
  5. 总结对比

在PHP中实现“双人操作”通常有几种常见的应用场景,我这里针对实时双人协作(如双人编辑文档、双人游戏对战等)给出几种主流实现方案:

最基础方案:轮询 + 数据库(适合学习/小规模)

// 用户A写入操作
// save_action.php
<?php
session_start();
$action = $_POST['action'];
$user_id = $_SESSION['user_id'];
// 存入数据库
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->prepare("INSERT INTO actions (user_id, action, created_at) VALUES (?, ?, NOW())");
$stmt->execute([$user_id, $action]);
// 返回给对方的最新状态
$stmt = $pdo->query("SELECT * FROM actions WHERE user_id != $user_id ORDER BY id DESC LIMIT 1");
$other_action = $stmt->fetch();
echo json_encode($other_action);
?>
<!-- 前端定时轮询 -->
<script>
function checkOtherAction() {
    fetch('check_actions.php')
        .then(res => res.json())
        .then(data => {
            if(data.action) {
                // 执行对方操作
                applyAction(data.action);
            }
        });
}
setInterval(checkOtherAction, 1000); // 每秒检查
</script>

推荐方案:WebSocket + Ratchet(实时双向)

安装依赖

composer require cboden/ratchet

服务端代码 (chat_server.php)

<?php
require 'vendor/autoload.php';
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class DualPlayer implements MessageComponentInterface {
    protected $clients;
    protected $rooms = [];
    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }
    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        echo "新连接: {$conn->resourceId}\n";
    }
    public function onMessage(ConnectionInterface $from, $msg) {
        $data = json_decode($msg);
        switch($data->type) {
            case 'join_room':
                // 加入房间
                $room = $data->room;
                $this->rooms[$room][] = $from;
                $from->room = $room;
                // 如果有两人,通知开始
                if(count($this->rooms[$room]) == 2) {
                    foreach($this->rooms[$room] as $client) {
                        $client->send(json_encode([
                            'type' => 'game_start',
                            'message' => '双方已就绪!'
                        ]));
                    }
                }
                break;
            case 'action':
                // 转发动作给房间内另一个人
                $room = $from->room;
                foreach($this->rooms[$room] as $client) {
                    if($client !== $from) {
                        $client->send(json_encode([
                            'type' => 'opponent_action',
                            'action' => $data->action
                        ]));
                    }
                }
                break;
        }
    }
    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        if(isset($conn->room)) {
            unset($this->rooms[$conn->room]);
        }
    }
    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}
// 启动WebSocket服务器
$server = IoServer::factory(
    new HttpServer(
        new WsServer(
            new DualPlayer()
        )
    ),
    8080
);
echo "WebSocket服务器运行在 ws://localhost:8080\n";
$server->run();

前端HTML (dual_game.html)

<!DOCTYPE html>
<html>
<head>双人协作</title>
</head>
<body>
    <div id="status">等待对手加入...</div>
    <div id="gameArea">
        <button onclick="sendAction('left')">←</button>
        <button onclick="sendAction('right')">→</button>
        <button onclick="sendAction('jump')">↑</button>
    </div>
    <div id="opponentAction"></div>
    <script>
        const ws = new WebSocket('ws://localhost:8080');
        const roomId = 'room_' + Math.floor(Math.random() * 1000);
        ws.onopen = () => {
            ws.send(JSON.stringify({
                type: 'join_room',
                room: roomId
            }));
        };
        ws.onmessage = (e) => {
            const data = JSON.parse(e.data);
            if(data.type === 'game_start') {
                document.getElementById('status').textContent = data.message;
            } else if(data.type === 'opponent_action') {
                document.getElementById('opponentAction').textContent = 
                    '对手执行: ' + data.action;
                // 这里可以写对手动作的视觉反馈
            }
        };
        function sendAction(action) {
            ws.send(JSON.stringify({
                type: 'action',
                action: action
            }));
        }
    </script>
</body>
</html>

启动服务端

php chat_server.php

然后用两个浏览器打开 dual_game.html,它们会配对到同一个房间。

文件锁方案(最简单的双人文件编辑)

<?php
// 双人编辑同一个文件
$filename = 'shared_data.json';
$fp = fopen($filename, 'r+');
// 获取独占锁
if (flock($fp, LOCK_EX)) {
    // 读取当前数据
    $data = json_decode(file_get_contents($filename), true);
    // 用户A修改
    $data['user1_score'] = $_POST['score'];
    // 写入回文件
    ftruncate($fp, 0);
    fwrite($fp, json_encode($data));
    fflush($fp);
    flock($fp, LOCK_UN);
}
fclose($fp);

推荐的生产级方案

  • Pusher (第三方WebSocket服务)
  • Socket.IO + PHP (通过Node.js中转)
  • Laravel Echo + Laravel WebSockets

总结对比

方案 延迟 实现难度 适合场景
轮询 简单 学习演示
WebSocket 中等 实时游戏/协作
文件锁 简单 双人编辑
Pusher 中等 生产环境

对于真正的实时双人操作,强烈建议使用WebSocket方案,如果只是简单的双人轮流操作(类似回合制),用HTTP请求+数据库就足够了。

抱歉,评论功能暂时关闭!