PHP向Node / Socket.IO服务器发送消息

8

我不太确定我的做法是否正确。我想继续使用我的Socket.IO服务器,而不想在Node中创建单独的HTTP服务器。在这种情况下,我能否创建一个PHP客户端,直接向Node Socket.IO服务器发送数据(例如:玩家从在线商店购买物品)?

我已经开始了以下内容:

<?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获取它?

希望我的问题有意义!

7个回答

12

我搜索了很多,并最终找到了一个相对简单的解决方案。

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

与许多其他解决方案尝试连接到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" => "something", "Data2" => "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/"

1
最终我成功地让它像这样工作: server.on('connection', (socket) => { socket.on('data', function( data) { data+=''; if (data && (data[0] == '{')) { data=JSON.parse(data); console.log(data.data); } }); }); - Shlomo
有趣!感谢反馈。我可能会试一下。如果这是问题,你可能需要尝试一下s.RemoteAddress部分。否则,请使用 console.log 连接以查看来自哪个地址。每当发送数据到服务器时,s.on('data') 都应该触发。 - user1274820
1
谢谢。最终我设法让它工作了。但是s.on('data')接收到了许多其他消息。所以我检查数据是否以'{'(JSON)开头,然后它就可以正常工作了。 但我仍在调查一个问题,由于某种原因,在几条消息之后,nodejs服务器会“卡住”,并且不会释放出站消息(从PHP到客户端)。 - Shlomo
我遇到了相同的问题(Node.js捕获额外信息)。我还没有测试过,但我认为在我的设置之前,它正在过滤ipv6流量,因此它看到了代理数据之间的差异。我认为将127.0.0.1更改为localhost可能会解决这个问题 - 希望尝试一下。同时,我也做了你所做的事情 - 检查它是否像有效的JSON一样,但显然这是一种不好的方法。 - user1274820
1
你好!我遇到了一个非常奇怪的问题,套接字连接没有问题,但是在socket.io服务器上没有触发任何.on('connection')事件。 - quiquelhappy
显示剩余2条评论

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!!';
}

3

适用于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/

我们如何设置频道名称? - dılo sürücü
我想使用 PHP curl 和 socket.emit 一起使用,可以吗? - dılo sürücü
1
也许这个答案也有帮助,可以更深入地解释:https://stackoverflow.com/a/65572463/936284 - Alexander Taubenkorb

3

我正在使用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();

是的,它会延迟或者不发送任何东西。你试过这个吗?https://github.com/rase-/socket.io-php-emitter 如果试过,它和elephant.io相比如何? - vee

1

ElephantIO 似乎已经停止维护。我找了很多发射器,这个 似乎是最快的,并且作者似乎很活跃,我建议按照以下步骤进行

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,但该软件包似乎快速且易于使用! 您可以使用由@user1274820提供的相同代码在socket.io服务器端进行测试


0

0

Socket IO已更改内部代码,因此所有来自V2的连接无法连接到V3,反之亦然。

Github上的所有库都使用V2协议,因此如果您正在使用最新的socketIO V3,则很难在这些库上运行它。

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

我制作了一个小教程,使用Symfony,您可以在此帖子上直接在php中使用CURL:

Socket.io 3 and PHP integration


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接