[英]Socket.io 3 and PHP integration
我使用 PHP SocketIO class 連接 NodeJS 應用程序並發送消息。 一切都與 Socket.io 2 完美配合,但在升級到版本 3 后,PHP 集成停止工作。
當我發送請求時,我收到以下回復:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hNcappwZIQEbMz7ZGWS71lNcROc=
但是我在 NodeJS 端看不到任何東西,即使我嘗試使用“連接”事件記錄與服務器的任何連接也是如此。
這是 PHP class:
class SocketIO
{
/**
* @param null $host - $host of socket server
* @param null $port - port of socket server
* @param string $action - action to execute in sockt server
* @param null $data - message to socket server
* @param string $address - addres of socket.io on socket server
* @param string $transport - transport type
* @return bool
*/
public function send($host = null, $port = null, $action= "message", $data = null, $address = "/socket.io/?EIO=2", $transport = 'websocket')
{
$fd = fsockopen($host, $port, $errno, $errstr);
if (!$fd) {
return false;
} //Can't connect tot server
$key = $this->generateKey();
$out = "GET $address&transport=$transport HTTP/1.1\r\n";
$out.= "Host: https://$host:$port\r\n";
$out.= "Upgrade: WebSocket\r\n";
$out.= "Connection: Upgrade\r\n";
$out.= "Sec-WebSocket-Key: $key\r\n";
$out.= "Sec-WebSocket-Version: 13\r\n";
$out.= "Origin: https://$host\r\n\r\n";
fwrite($fd, $out);
// 101 switching protocols, see if echoes key
$result= fread($fd,10000);
preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $result, $matches);
$keyAccept = trim($matches[1]);
$expectedResonse = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$handshaked = ($keyAccept === $expectedResonse) ? true : false;
if ($handshaked){
fwrite($fd, $this->hybi10Encode('42["' . $action . '", "' . addslashes($data) . '"]'));
fread($fd,1000000);
return true;
} else {return false;}
}
private function generateKey($length = 16)
{
$c = 0;
$tmp = '';
while ($c++ * 16 < $length) { $tmp .= md5(mt_rand(), true); }
return base64_encode(substr($tmp, 0, $length));
}
private function hybi10Encode($payload, $type = 'text', $masked = true)
{
$frameHead = array();
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
$frameHead[0] = 129;
break;
case 'close':
$frameHead[0] = 136;
break;
case 'ping':
$frameHead[0] = 137;
break;
case 'pong':
$frameHead[0] = 138;
break;
}
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
if ($frameHead[2] > 127) {
$this->close(1004);
return false;
}
} elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
}
謝謝你的幫助!
我對 github 上存在的所有庫都遇到了同樣的問題,問題是它們被放棄或沒有更新到 socket.io V3。
在 socket.io 文檔中說:
TL;DR:由於幾個重大更改,v2 客戶端將無法連接到 v3 服務器(反之亦然)
要解決此問題,您需要了解 socket.io 客戶端的工作原理,這很容易,因為在協議文檔的示例會話部分中。
要解決這個問題,您需要忘記 fsockopen 和 fwrite 函數,您需要直接使用 CURL 執行協議文檔中提到的請求。
請求 n°1
得到
url :/socket.io/?EIO=4&transport=polling&t=N8hyd7H
打開數據包:打開php和socket.io服務器之間的連接。 服務器將返回一個名為“sid”的“會話 id”,您將把它添加到 url 查詢中以進行后續查詢。
請求 n°2
郵政
url :/socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
帖子正文:'40'
命名空間連接請求:您需要在正文中發送數字 40,作為字符串,這意味着您要連接到 socket.io “消息”類型
請求 n°3
得到
url :/socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
命名空間連接批准:如果連接成功或出現錯誤,這將返回,這是 socket.io 服務器授權您的連接時,如果您需要令牌。
請求 n°4
郵政
url :/socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=sessionIdFromRequest1
帖子正文:42[事件,數據]
例如 42["notifications","Hi, Im a notification"] 相當於 socket.emit(event,data)向服務器發送消息:將您的消息發送到 socket.io 服務器。
這是一個使用 Symfony 5.2 和 HttpClientInterface 的 BASIC 示例:
public function sendToSocket(HttpClientInterface $client)
{
$first = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=N8hyd6w');
$res = ltrim($first->getContent(), '0');
$res = json_decode($res, true);
$sid = $res['sid'];
$second = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '40'
]);
$third = $client->request('GET', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid);
$fourth = $client->request('POST', 'http://localhost:3000/socket.io/?EIO=4&transport=polling&sid='.$sid, [
'body' => '42["notifications","Hi, Im a notification"]'
]);
}
如您所見,這非常簡單,而且您不需要麻煩的“復制粘貼”庫。 我說“復制粘貼”是因為都使用相同的代碼打開 de socket 並發送信息,但沒有一個與 socket.io V3 兼容。
這是一張圖片,證明給定的代碼在 2021 年 1 月 4 日使用 php 7.4、symfony 5.2 和 socket.io V3。
這是我在節點中的測試服務器
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.on('connection', function (socket) {
console.log("New Connection with transport", socket.conn.transport.name);
socket.on('notifications', function (data) {
console.log(data);
});
});
http.listen(3000, () => {
console.log('Server started port 3000');
});
我需要說的是,如果您想向 socket.io 服務器發送“單向”消息(例如新通知或不需要永久連接的任何內容),則此解決方案非常有效,只是“一擊”而已。
來自墨西哥的快樂編碼和問候。
第一列是 Postman 向 php 服務器發出請求,模擬服務器端事件,就像創建一個新問題一樣。 在響應中是您需要發出的 4 個請求的響應正文的轉儲。
第二列是socket.IO節點服務器運行在3000端口
最后一列是 chrome 控制台,模擬連接到 socket.IO 服務器的用戶,通過 websocket 在“問題”事件中尋找通知。
根據https://github.com/rase-/socket.io-php-emitter/issues/36嘗試 robgonnella/socket.io-php-emitter
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.