PHP 套接字 - 接受多个连接

2024-01-12

我正在尝试创建一个简单的客户端/服务器应用程序,因此我正在尝试使用 PHP 中的套接字。

现在我有一个简单的 C# 客户端,可以很好地连接到服务器,但我一次只能将一个客户端连接到该服务器(我在网上找到了这个代码示例,并为了测试目的对其进行了一些调整)。

有趣的是,我发现了同样的问题,基于这里的相同示例:https://stackoverflow.com/questions/10318023/php-socket-connections-cant-handle-multiple-connection https://stackoverflow.com/questions/10318023/php-socket-connections-cant-handle-multiple-connection

我试图理解它的每个部分,并且我即将了解它的详细工作原理,但由于某种原因,当我连接第二个客户端时,第一个客户端断开连接/崩溃。

谁能给我一些疯狂的想法或指出我应该看的地方?

<?php
// Set time limit to indefinite execution
set_time_limit (0);
// Set the ip and port we will listen on
$address = '127.0.0.1';
$port = 9000;
$max_clients = 10;
// Array that will hold client information
$client = array();
// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port) or die('Could not bind to address');
// Start listening for connections
socket_listen($sock);
// Loop continuously
while (true) {
    // Setup clients listen socket for reading
    $read[0] = $sock;
    for ($i = 0; $i < $max_clients; $i++)
    {
        if (isset($client[$i]))
        if ($client[$i]['sock']  != null)
            $read[$i + 1] = $client[$i]['sock'] ;
    }
    // Set up a blocking call to socket_select()
    $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL);
    /* if a new connection is being made add it to the client array */
    if (in_array($sock, $read)) {
        for ($i = 0; $i < $max_clients; $i++)
        {
            if (!isset($client[$i])) {
                $client[$i] = array();
                $client[$i]['sock'] = socket_accept($sock);
                echo("Accepting incoming connection...\n");
                break;
            }
            elseif ($i == $max_clients - 1)
                print ("too many clients");
        }
        if (--$ready <= 0)
            continue;
    } // end if in_array

    // If a client is trying to write - handle it now
    for ($i = 0; $i < $max_clients; $i++) // for each client
    {
        if (isset($client[$i]))
        if (in_array($client[$i]['sock'] , $read))
        {
            $input = socket_read($client[$i]['sock'] , 1024);
            if ($input == null) {
                // Zero length string meaning disconnected
                echo("Client disconnected\n");
                unset($client[$i]);
            }
            $n = trim($input);
            if ($n == 'exit') {
                echo("Client requested disconnect\n");
                // requested disconnect
                socket_close($client[$i]['sock']);
            }
            if(substr($n,0,3) == 'say') {
                //broadcast
                echo("Broadcast received\n");
                for ($j = 0; $j < $max_clients; $j++) // for each client
                {
                    if (isset($client[$j]))
                    if ($client[$j]['sock']) {
                        socket_write($client[$j]['sock'], substr($n, 4, strlen($n)-4).chr(0));
                    }
                }
            } elseif ($input) {
                echo("Returning stripped input\n");
                // strip white spaces and write back to user
                $output = ereg_replace("[ \t\n\r]","",$input).chr(0);
                socket_write($client[$i]['sock'],$output);
            }
        } else {
            // Close the socket
            if (isset($client[$i]))
            echo("Client disconnected\n");
            if ($client[$i]['sock'] != null){ 
                socket_close($client[$i]['sock']); 
                unset($client[$i]); 
            }
        }
    }
} // end while
// Close the master sockets
echo("Shutting down\n");
socket_close($sock);
?>

当前的最佳答案是错误的,您不需要多个线程来处理多个客户端。您可以使用非阻塞 I/O 和stream_select / socket_select处理来自客户端的可操作的消息。我建议使用stream_socket_*功能超过socket_*.

虽然非阻塞 I/O 工作得很好,但您不能进行任何涉及阻塞 I/O 的函数调用,否则阻塞 I/O 会阻塞整个进程,并且所有客户端都会挂起,而不仅仅是一个。

这意味着所有 I/O 都必须是非阻塞的或保证非常快(这并不完美,但可能是可以接受的)。因为不仅你的套接字需要使用stream_select,但您需要选择all打开流,我推荐一个库,它提供注册读取和写入监视程序的功能,这些监视程序在流变得可读/可写时执行。

有多种此类框架可用,最常见的是ReactPHP https://reactphp.org and Amp https://amphp.org/。底层事件循环非常相似,但 Amp 在这方面提供了更多功能。

两者之间的主要区别在于 API 的方法。虽然 ReactPHP 到处都使用回调,但 Amp 试图通过使用协程并针对此类用法优化其 API 来避免它们。

Amp's “入门”指南基本上就是关于这个主题的。您可以阅读完整指南here https://amphp.org/getting-started/tcp-chat/。我将在下面提供一个工作示例。

<?php

require __DIR__ . "/vendor/autoload.php";

// Non-blocking server implementation based on amphp/socket.

use Amp\Loop;
use Amp\Socket\ServerSocket;
use function Amp\asyncCall;

Loop::run(function () {
    $uri = "tcp://127.0.0.1:1337";

    $clientHandler = function (ServerSocket $socket) {
        while (null !== $chunk = yield $socket->read()) {
            yield $socket->write($chunk);
        }
    };

    $server = Amp\Socket\listen($uri);

    while ($socket = yield $server->accept()) {
        asyncCall($clientHandler, $socket);
    }
});

Loop::run()运行事件循环并监视计时器事件、信号和可操作流,这些可以通过以下方式注册Loop::on*()方法。使用以下命令创建服务器套接字Amp\Socket\listen(). Server::accept()返回一个Promise它可用于等待新的客户端连接。一旦客户端被接受,它就会执行一个协程,从客户端读取数据并将相同的数据回显给客户端。有关更多详细信息,请参阅 Amp 的文档。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PHP 套接字 - 接受多个连接 的相关文章

  • 显示带有 id 的内部连接的名称[重复]

    这个问题在这里已经有答案了 我有这个查询 select from countrysegments inner join country on countrysegments country id country id inner join
  • 使用 .htaccess 进行 PHP 设置时出现 500 内部服务器错误

    当我使用时 htaccess对于以下 PHP 设置 我得到500 Internal Server Error访问网站时 中的代码 htaccess file php flag display errors off php flag log
  • php / phpDoc - @return $this 类的实例?

    如何在 phpDoc 中将方法标记为 返回当前类的实例 在下面的示例中 我的 IDE Netbeans 将看到 setSomething 始终返回foo object 但如果我扩展对象 情况就不是这样了 它会返回 this 在第二个示例中是
  • 通过字符串操作预防 PHP SQL 注入[重复]

    这个问题在这里已经有答案了 可能的重复 PHP 中防止 SQL 注入的最佳方法 https stackoverflow com questions 60174 best way to prevent sql injection in php
  • 测试 CodeIgniter 会话变量的正确方法是什么?

    获取以下代码片段 测试确保会话变量不为空的最佳方法是什么 如果稍后在我的脚本中 我调用以下内容 第一个打印正确 但在第二个我收到消息 未定义的变量 已登录 我尝试过使用 empty and isset 但两者均未成功 我还尝试使用向后执行
  • WAMP PHP 启动错误:无法加载动态库

    我的 WAMP 服务器昨天工作得非常好 今天我不得不重新启动计算机 但有些事情搞砸了 服务器从未完全启动 任务栏图标保持橙色 我的项目将打开 但仅加载 HTML 而不加载 PHP 这是我的 PHP 错误日志 04 Oct 2014 01 3
  • PHP 可以解压缩使用 .NET GZipStream 类压缩的文件吗?

    我有一个 C 应用程序 它与基于 PHP 的 SOAP Web 服务进行通信以进行更新和许可 我现在正在开发一个反馈系统 供用户通过软件自动提交错误和跟踪日志 根据我之前发布的问题 我认为 Web 服务将是实现此目的的最佳方法 最有可能以最
  • 通过 PDO 将双精度数插入 MySQL 时精度损失

    我遇到了这种非常烦人的行为 我想知道我是否做错了什么 或者这是否是故意的 如果是的话 为什么 每当我在 php 5 3 中有一个 double 类型的变量 并且想将其插入到数据库 MYSQL 5 0 的 double 类型字段中时 该值总是
  • 通过 PEAR 安装 PHPUnit

    我安装时遇到问题PHPUnit 3 4 6 via 梨1 9 0 当我发现频道后pear phpunit de并尝试使用以下命令之一 pear 安装 phpunit PHPUnit pear 安装 alldeps phpunit PHPUn
  • 计算帖子中使用 WordPress 短代码的次数

    我有以下 WordPress 短代码功能 function wp shortcode static i 1 return i i return return add shortcode shortcode wp shortcode 这很好用
  • 重用 PDO 语句 var 会使进程崩溃

    我重用一个变量来存储两个不同的 PDO mysql 语句 stmt dbh gt prepare SELECT stmt gt execute stmt dbh gt prepare UPDATE crash here Error in o
  • Chart.js - 使用 mysql 和 php 从数据库获取数据

    我正在尝试将静态数据转换为使用数据库结果 我将使用MySQL and PHP 示例代码 var randomScalingFactor function return Math round Math random 100 var lineC
  • POSTed 数组保留其值

    今天 我注意到我的一个 WordPress 安装的行为方式非常奇怪 每当我从仪表板更新帖子时 它的一些额外输入都会保留它们已有的值 所有这些都是多维数组 例如 post php post 123 action 编辑
  • 即使在双引号内或使用 PHP_EOL,PHP echo 也不打印换行符

    这是一张照片剪辑 我认为它很清楚 这真让我抓狂 我似乎无法让 php 在我的本地主机 使用 xampp 或在线 PHP 解释器上打印换行符 如果你想在 php 中使用 HTML 标签 试试这个 echo dkljaks aalksja kl
  • 验证 LDAPS 连接的自签名证书

    我想从 Linux Linux 3 2 0 4 amd64 1 SMP Debian 3 2 51 1 x86 64 GNU Linux 客户端到 Windows 2012 服务器建立安全的 ldap 连接 ldaps 以更改活动中的用户密
  • 多维数组 PHP 内爆 [重复]

    这个问题在这里已经有答案了 就我的数据结构而言 我有一个 communications 数组 每个 communications id 本身包含三部分信息 id score 和 content 我想内爆这个数组以获得逗号分隔的 id 列表
  • 如何使用 PHP 将字符串按大写字母分解?

    我有一个字符串 CamelCaseString 我想对大写字母进行explode split 或一些更好的方法来将该字符串分解为单个单词 最简单的方法是什么 解决方案更新 此链接指向一个略有不同的问题 但我认为答案通常比本页当前问题的答案更
  • Laravel 4.2 Composer 安装错误:“无法扫描类”

    我想通过 Composer 在新的 Laravel 4 2 安装上安装一些软件包 但是 我遇到了例外 这是我的作曲家文件 name laravel laravel description The Laravel Framework keyw
  • 无法访问 WordPress 中声明的全局变量

    我有以下代码 g value something print g value function get value global g value print g value print get value 当我在独立的 PHP 脚本中运行它
  • mysql_query 保留返回时在表中创建的数据类型?

    我在mysql中有一个表 CREATE TABLE user id INT name VARCHAR 250 我查询表 result mysql query SELECT id name FROM user 我收集结果 while row

随机推荐

  • 使用 Jquery 附加多个 html 元素

    我对 jQuery 很陌生 想知道是否有人可以建议我最佳实践 我希望将一个 div 元素附加到页面 其中包含大量 html 并且不确定实现此目的的最佳方法是什么 或者是否建议使用 jquery 例如 如果我想使用 jquery 将以下代码附
  • 函数模板的多个定义

    假设头文件定义了一个函数模板 现在假设有两个实现文件 include这个头 并且每个都调用了函数模板 在两个实现文件中 函数模板都使用相同的类型进行实例化 header hh template
  • 加载页面时显示加载动画微调器

    我想在 JQueryMobile 页面中显示加载动画微调器 该页面是通过 ajax 关闭加载的 页面已加载data ajax false or rel external 我试穿了pagebeforecreate and pageshow事件
  • Javascript样式对象将复杂的颜色名称转换为rgb

    当将复杂的 CSS 颜色名称应用于 DOM 元素时 有没有一种方法可以覆盖 javascript 将复杂的 CSS 颜色名称转换为 RGB 值的方式document getElementById xxx style object 例如 设置
  • php imagemagick 创建平铺金字塔 TIFF

    好吧 正如标题所说 我遇到了一个问题 我的测试函数是这样的 imagePath tmp 511a3874a0da1 pngName imagePath png tifName imagePath tif tempImg new Imagic
  • 具有多个子类型的 Scala 泛型(元组)类型

    我正在 Scala 中编写一个数据结构 基本上是一个哈希图 它将采用一个元组 每次可能有不同数量的参数 并用它做一些事情 为了一般地实现这一点 我定义了一个类型 type T lt Tuple1 with Tuple2 with Tuple
  • 更改应用程序中的 Cocoa 显示名称?

    如何更改 OS X 上 Dock 中应用程序上方显示的名称 我尝试过重命名目标并重命名我的项目 而且 我已经用 Google 搜索过它 经过进一步谷歌搜索后 我发现了这一点 项目 gt 编辑活动目标 gt 打包 gt 产品名称 虽然我第一次
  • Paytm sdk ios集成打开Paytm付款表格?

    在 iOS Xcode 7 中集成 Paytm sdk 2 1 并配置为进行支付 我有一个表格 其中需要填写金额和其他字段 然后有一个 Payment 按钮 这是我正在使用的代码 Step 1 Create a default mercha
  • jqGrid 卡在加载中?

    我正在尝试使用 JQuery 插件jqGrid http www trirand com blog 使用 asp net mvc 应用程序 我在下面向网格传递一个 JSON 对象format http www secondpersonplu
  • 创建 UIImageView 时上下文无效

    当我尝试创建 UIImageView 时出现错误 看看这段代码 UIImage backgroundPanel UIImage imageNamed loginPanelBackground png resizableImageWithCa
  • Azure API 管理:验证 jwt 令牌范围

    我们希望使用 validate jwt 策略保护 API 操作调用 但当我使用 required claims 检查范围时遇到问题 示例 我有一个令牌 其范围包括多个值 例如 xxx READ xxx WRITE yyy READ yyy
  • 如何解析 Excel 文件以提供与视觉上显示的数据完全相同的数据?

    我使用的是 Rails 5 Ruby 2 4 我想阅读 xls 文档 并且希望将数据转换为 CSV 格式 就像在 Excel 文件中显示的那样 有人推荐我使用 Roo 所以我就这么做了 book Roo Spreadsheet open f
  • 将标题行写入 csv python

    如何将标题添加到 csv 的第一行 我的解决方案目前附加了所有内容 就像是 writer writerow DataA DataB DataC DATA D 0 我觉得有一种简单的方法可以做到这一点 但我忽略了显而易见的事情 我在网上查看了
  • 为什么 JSLint 更喜欢点表示法而不是方括号?

    我一直在检查我的一些代码 并收到一些错误 说最好使用点表示法 我发现我使用的是方括号符号 从这篇精彩的文章中可以清楚地看出 https stackoverflow com questions 2001360 javascript dot n
  • 实体框架-存储过程返回值

    我正在尝试获取存储过程的返回值 以下是此类存储过程的示例 select Name IsEnabled from dbo something where ID ID if rowcount 0 return 1 return 这是一个简单的选
  • ANDROID,从Web服务器解析JSON数据并显示在ListView上

    我正在尝试显示 JSON url 链接的 JSON 结果 目前 当我加载时 它什么也不显示 只是空白页面 这是我获取有关 JSON 信息的来源 http adblogcat com parse json data from a web se
  • Orchard CMS:注销(注销)确认页面

    当用户注销前端时是否会触发一个事件 如何使用该事件将用户重定向到特定视图或页面 我希望用户在注销后收到一条消息 显示 您已成功注销 与往常一样 使用 Orchard 有不止一种方法可以做到这一点 方法 1 覆盖用户形状 当您注销时 您将被重
  • 从另一个线程读取复选框状态

    我正在尝试从 WPF 中的 BackgroundWorker 读取复选框的值 这不起作用 bool isSleepChecked checkBoxSleep Dispatcher Invoke DispatcherPriority Norm
  • 模块的循环依赖

    我想用 F 编写一个解析器 并且由于某些原因我必须使用 Antlr 这意味着我必须定义一个Visitor我想要解析的每个 AST 节点的类 现在我遇到的问题是 存在一些具有循环依赖关系的规则 例如 boolExpr boolTerm or
  • PHP 套接字 - 接受多个连接

    我正在尝试创建一个简单的客户端 服务器应用程序 因此我正在尝试使用 PHP 中的套接字 现在我有一个简单的 C 客户端 可以很好地连接到服务器 但我一次只能将一个客户端连接到该服务器 我在网上找到了这个代码示例 并为了测试目的对其进行了一些