【问题标题】:PHP sending message to Node / Socket.IO serverPHP 向 Node / Socket.IO 服务器发送消息
【发布时间】:2014-03-16 13:14:01
【问题描述】:

我不太确定我是否以正确的方式处理这件事。我想坚持使用我的 Socket.IO 服务器,并且不想在节点中创建单独的 HTTP 服务器。有了这个,我可以创建一个可以将数据(例如:玩家从网上商店购买物品)直接发送到节点 Socket.IO 服务器的 PHP 客户端吗?

我已经开始这样做了:

<?php

class communicator {
   public function connect($address, $port){
      $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

      if($socket){
          try {
              socket_connect($socket, $address, $port);
          } catch(Exception $e){
              throw new Exception($e->getMessage());
          }
      }else{
          throw new Exception('Could not create socket.');
      }
}

?>

套接字似乎可以很好地连接到节点服务器,但我怎样才能开始直接从 PHP 客户端接收数据?

例如:假设我使用socket_write 向服务器发送消息。我如何通过 Socket.IO 获得它?

希望我的问题有意义!

【问题讨论】:

    标签: php node.js sockets socket.io


    【解决方案1】:

    我使用http://elephant.io/ 在 php 和 socket.io 之间进行通信,我只有建立连接的时间问题,3 或 4 秒完成发送数据。

    <?php
    
    require( __DIR__ . '/ElephantIO/Client.php');
    use ElephantIO\Client as ElephantIOClient;
    
    $elephant = new ElephantIOClient('http://localhost:8080', 'socket.io', 1, false, true, true);
    
    $elephant->init();
    $elephant->emit('message', 'foo');
    $elephant->close();
    

    【讨论】:

    【解决方案2】:

    ElephantIO 还没有更新到最新的 Socket.IO。

    因为socket.io v0.9和v1.x有很大的不同。

    这里是Socket.io v1.0:http://socket.io/blog/introducing-socket-io-1-0/

    已更新:此链接可能会有所帮助:https://github.com/psinetron/PHP_SocketIO_Client

    【讨论】:

      【解决方案3】:

      使用最新版本的ElephantIO,我设法发送了一条消息:

      include_once("vendor/autoload.php");
      use ElephantIO\Exception\ServerConnectionFailureException;
      
      $client = new \ElephantIO\Client(new \ElephantIO\Engine\SocketIO\Version1X('http://localhost:9000'));
      
      try
      {
          $client->initialize();
          $client->emit('message', ['title' => 'test']);
          $client->close();
      }
      catch (ServerConnectionFailureException $e)
      {
          echo 'Server Connection Failure!!';
      }
      

      【讨论】:

        【解决方案4】:

        这个我找了好久,终于找到了一个比较简单的解决方案。

        这不需要任何额外的 PHP 库 - 它只使用套接字。

        与其像许多其他解决方案那样尝试连接到 websocket 接口,只需连接到 node.js 服务器并使用.on('data') 接收消息。

        然后,socket.io 可以将其转发给客户。

        在 Node.js 中检测来自 PHP 服务器的连接,如下所示:

        //You might have something like this - just included to show object setup
        var app = express();
        var server = http.createServer(app);
        var io = require('socket.io').listen(server);
        
        server.on("connection", function(s) {
            //If connection is from our server (localhost)
            if(s.remoteAddress == "::ffff:127.0.0.1") {
                s.on('data', function(buf) {
                    var js = JSON.parse(buf);
                    io.emit(js.msg,js.data); //Send the msg to socket.io clients
                });
            }
        });
        

        这是非常简单的 php 代码 - 我将它封装在一个函数中 - 你可能会想出更好的东西。

        请注意,8080 是我的 Node.js 服务器的端口 - 您可能需要更改。

        function sio_message($message, $data) {
            $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            $result = socket_connect($socket, '127.0.0.1', 8080);
            if(!$result) {
                die('cannot connect '.socket_strerror(socket_last_error()).PHP_EOL);
            }
            $bytes = socket_write($socket, json_encode(Array("msg" => $message, "data" => $data)));
            socket_close($socket);
        }
        

        你可以这样使用它:

        sio_message("chat message","Hello from PHP!");

        您还可以发送转换为 json 并传递给客户端的数组。

        sio_message("DataUpdate",Array("Data1" =&gt; "something", "Data2" =&gt; "something else"));

        这是一种“信任”您的客户端从服务器获取合法消息的有用方法。

        您还可以让 PHP 传递数据库更新,而无需数百个客户端查询数据库。

        我希望我能早点找到这个 - 希望这会有所帮助! ?

        后期编辑:不确定这是否是要求,但我将此添加到我的 http.conf(Apache 配置)中:

        我想我会把代码放在这里以防它对任何人有帮助。

        在 httpd.conf 我添加了以下代码:

        RewriteEngine On
        RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
        RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
        RewriteRule .* ws://localhost:8080%{REQUEST_URI} [P]
        
        ProxyRequests Off
        ProxyPass        /socket.io http://localhost:8080/socket.io retry=0
        ProxyPassReverse /socket.io http://localhost:8080/socket.io retry=0
        
        ProxyPass "/node" "http://localhost:8080/"
        

        【讨论】:

        • 我正在尝试使用您的解决方案,但它对我不起作用。我从 PHP 发送的消息确实到达了 socket.io 客户端,但它没有进入: s.on('data', function) 我不明白什么会触发它,因为在 PHP 我没有看到像 emit('data',buff) 之类的东西,但只有一些 json 与一个项目“数据”,我认为它不相关(尽管我还在我的 json 中添加了一个“数据”项目)。
        • 最终我设法让它像这样工作: server.on('connection', (socket) => { socket.on('data', function(data) { data+=''; if (data && (data[0] == '{')) { data=JSON.parse(data); console.log(data.data); } }); });
        • 有趣!感谢您的反馈。我可能会试一试。如果这是问题所在,您可能需要使用 s.RemoteAddress 部分。否则,console.log 连接以查看它来自哪个地址。 s.on('data' 应该在数据发送到服务器时触发。
        • 谢谢。最终我设法使它工作。但是 s.on('data') 收到许多其他消息。所以我检查数据是否以'{'(JSON)开头,然后它工作正常。但是我仍然在研究一个问题,由于某种原因,在收到几条消息后,nodejs 服务器“卡住”了,并且没有释放出消息(从 PHP 到客户端)。
        • 您好!对我来说真的很奇怪,套接字连接没有问题,但是在 socket.io 服务器上没有触发 .on('connection') 事件
        【解决方案5】:

        ElephantIO 似乎已经死了。找了很多发射器,this one好像是最快的,作者好像很活跃,建议做如下

        composer require ashiina/socket.io-emitter
        

        然后,在你的 php 文件中

        public function sendMessage($name, $data)
        {
        
            $emitter = new SocketIO\Emitter(array('port' => strval($this->port), 'host' => $this->ipv4));
            $emitter->broadcast->emit($name, $data);
            
        }
        

        当然你需要设置一个$port 和一个$ipv4,但是这个包似乎又快又好用!您可以在 socket.io 服务器端使用@user1274820 提供的相同代码来测试它

        【讨论】:

          【解决方案6】:

          Socket IO 内部代码发生了变化,所以 V2 的所有连接都无法连接到 V3,反之亦然。

          github 上的所有库都使用 V2 协议,所以如果您使用的是最新的 socketIO V3,那么在这些库上工作会很痛苦。

          最简单的解决方案是使用“轮询”和 CURL。

          我用symfony做了一个小教程,你可以直接在php上使用CURL,在这篇文章中:

          Socket.io 3 and PHP integration

          【讨论】:

            【解决方案7】:

            适用于 socket.io 版本 3 的新答案

            https://github.com/socketio/socket.io-protocol#sample-session

            工作服务器示例:

            直接运行服务器/usr/local/bin/node /socket_io/server/index.js

            或通过'forever'运行服务器

            /usr/local/bin/forever -a -l /logs/socket.io/forever-log.log -o /logs/socket.io/forever-out.log -e /media/flashmax/var/logs/socket.io/forever-err.log start /socket_io/server/index.js
            

            index.js 文件内容(接收请求):

            var io = require('/usr/local/lib/node_modules/socket.io')(8000);
            //25-01-2021
            // socket.io v3
            //iptables -I INPUT 45 -p tcp -m state --state NEW -m tcp --dport 8000 -j ACCEPT
            //iptables -nvL --line-number
            io.sockets.on('connection', function (socket) {
                socket.on('router', function (channel,newmessage) {
            //  console.log('router==========='+channel);
            //  console.log('router-----------'+newmessage);
                socket.broadcast.to(channel).emit('new_chat',newmessage);
                });
                socket.on('watch_for_me', function (chanelll) {
                    socket.join(chanelll);
            //console.log('user watch new '+chanelll);
                });
                socket.on('dont_watch', function (del_chanelll) {
                    socket.leave(del_chanelll);
            //console.log('user go out' +del_chanelll);
                });
            }); 
            

            从 PHP 发送的脚本

            <?php
            //  php -n /socket_io/test.php
            //  dl('curl.so');
            //  dl('json.so');
            
            $site_name='http://127.0.0.1:8000/socket.io/?EIO=4&transport=polling&t=mytest';
            // create curl resource
            $ch = curl_init();
            
            //Request n°1 (open packet)
            // set url
            curl_setopt($ch, CURLOPT_URL, $site_name);
            //return the transfer as a string
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            // $output contains the output string
            $output = curl_exec($ch);
            $output=substr($output, 1);
            $decod=json_decode($output);
            
            //Request n°2 (namespace connection request):
            $site_name2=$site_name.'&sid='.$decod->sid;
            curl_setopt($ch, CURLOPT_URL, $site_name2);
            curl_setopt($ch, CURLOPT_POST, true);
            // 4           => Engine.IO "message" packet type
            // 0           => Socket.IO "CONNECT" packet type
            curl_setopt($ch, CURLOPT_POSTFIELDS, '40');
            $output = curl_exec($ch);
            
            // Request n°3 (namespace connection approval)
            if ($output==='ok')  {
              curl_setopt($ch, CURLOPT_URL, $site_name2);
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
              $output = curl_exec($ch);
            }
            
            // Request n°4 socket.emit('hey', 'Jude') is executed on the server:
            if ($output==='ok')  {
            curl_setopt($ch, CURLOPT_URL, $site_name2);
            curl_setopt($ch, CURLOPT_POST, true);
            
            $message_send='{\\"uuuuu\\": \\"000\\",\\"ryyyd\\":\\"999\\",\\"hyyya\\":\\"<div class=\'fro9\'>llll</div><div class=\'9ig\'>iiiii</div><div class=\'ti9g\'>iiiii</div>\\"}';
            
            curl_setopt($ch, CURLOPT_POSTFIELDS, "42[\"router\",\"chanel@id\",\"$message_send\"]");
            $output = curl_exec($ch);
            print_r($output);
            }
            
            $message_send='send 2 message';
            curl_setopt($ch, CURLOPT_POSTFIELDS, "42[\"router\",\"chanel@id\",\"$message_send\"]");
            $output = curl_exec($ch);
            
            
            $message_send='send 3 message';
            curl_setopt($ch, CURLOPT_POSTFIELDS, "42[\"router\",\"chanel@id\",\"$message_send\"]");
            $output = curl_exec($ch);
            
            // close curl resource to free up system resources
            curl_close($ch);  
            ?>
            

            奖励:apache 的设置 -> mod_proxy 访问 socket.io 可用于虚拟主机或整个服务器。

            RewriteEngine on
            RewriteCond %{QUERY_STRING} transport=polling    [NC]
            RewriteRule /(.*)$ http://127.0.0.1:8000/$1 [P,L]
            ProxyRequests off
            ProxyPass /socket.io/ ws://127.0.0.1:8000/socket.io/
            ProxyPassReverse /socket.io/ ws://127.0.0.1:8000/socket.io/
            

            【讨论】:

            • 我们如何设置频道名称?
            • 我想使用套接字。用 PHP curl 发射?
            • 也许这个答案也有帮助,更深入地解释这一点:stackoverflow.com/a/65572463/936284
            猜你喜欢
            • 2012-04-22
            • 1970-01-01
            • 2013-09-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多