問題描述
我正在嘗試從 PHP 向我的 websocket aws api 網關發送一個即發即棄的請求.
我設置了一個名為sendmessage"的操作.
這是我正在使用的代碼:
$protocol = "ssl";$host = ".amazonaws.com";$端口 = 443;$path = "//";$超時= 2000;$socket = pfsockopen($protocol . "://" . $host, $port,$errno, $errstr, $timeout);$content = "{'action': 'sendmessage', 'data': 'test'}";$body = "POST $path HTTP/1.1
";$body .= "主機:$host
";$body .= "內容類型:應用程序/json
";$body .= "內容長度:" .strlen($content) ."
";$body .= "連接:關閉
";$body .= $content;$body .= "
";fwrite($socket, $body);
然而,什么也沒發生.
如果我使用 wscat,例如:
wscat -c wss://.amazonaws.com/>{'動作':'發送消息','數據':'測試'}>
效果很好.
我的 php 代碼做錯了什么?
注意:我需要保持套接字連接(使用 pfsockopen 函數時的方式).
由于你沒有提供handpoint鏈接,這里有一些注意事項,請自行測試!
我猜問題出在wss
部分,php需要先檢索證書,這樣才能加密數據.
您的代碼應該可以在 ws://
流上正常工作.
要連接到常規 ws://
流,只需使用 fsockopen().
但是要連接到 wss://
secure websocket 流,使用 php,沒有庫,我們需要先創建一個隧道,通過查詢 公鑰與stream_socket_client.
這是一種握手機制.這可以按如下方式完成.
注意第一個 ssl://
調用.這是TLS 1.0 協議.
輸出應如下所示:
string(44) HTTP/1.1 101 Web Socket 協議握手"string(21) 連接:升級"string(37) 日期:2019 年 12 月 12 日星期四 04:06:27 GMT"string(52) "Sec-WebSocket-Accept: fTYwcEa6D9kJBtghptkz1e9CtBI="string(25) 服務器:Kaazing 網關";string(20) 升級:websocket";
相同的基本代碼,另一個示例,從 Binance wss://
流中提取數據.
我們還可以使用帶有 tls 的 TLS 1.2://
握手代替.適用于大多數服務器.
這是一種從 php 檢索僅遠程手持設備的 ssl RSA 公鑰的方法.可用于加速以后的連接.
真的,capture_peer_cert_chain"=>真的];$a = stream_context_create([ssl"=>$opt]);$b = stream_socket_client("ssl://stream.binance.com:9443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $a);$cont = stream_context_get_params($b);$key = openssl_pkey_get_public($cont[options"][ssl"][peer_certificate"]);$c = openssl_pkey_get_details($key);var_dump($c["key"]);
輸出類似:
-----開始公鑰--MIIBIjANBgkqhki(...)7aEsFtUNkwM5R5b1mpqzAwqHyvdamJx20bT6SS6PYXSr/dv8ak1d4e2Q0nIa1O7l3w0bZZ4wnp5B8Z+tjPd1W8uaZoRO2iVkPMh2yPlj0mmtUw1YlfDyutH/t4FlRCDiD4JjdREQGs381/+jbkdjl2SIb1IyNiCdAXA6zsqxwIDAQAB-----結束公鑰-----
可能還有其他怪癖,當然,我們需要主要的handpoint^.很高興測試一下.否則祝你好運,關于這個主題的文檔非常缺乏.
這是仍然是一個新生的協議(2011 年!).最好的細節在 RFC 規范中:
<塊引用>WebSocket 協議由 IETF 標準化為 RFC 64552011
關于握手,必須由GET請求發起.
<塊引用>客戶端將發送一個非常標準的 HTTP 請求,其標頭為看起來像這樣(HTTP 版本必須是 1.1 或更高版本,并且方法必須是GET)
Writing_WebSocket_servers#Client_handshake_request>
簡而言之:
<塊引用>如果未加密的 WebSocket 流量流經顯式或不支持 WebSockets 的透明代理服務器,連接可能會失敗.
WebSocket#Proxy_traversal
Transport_Layer_Security#Digital_certificates
I'm trying to send a fire-and-forget request from PHP to my websocket aws api gateway.
I've set up an action called "sendmessage".
This is the code I'm using:
$protocol = "ssl";
$host = "<myendpoint>.amazonaws.com";
$port = 443;
$path = "/<mystage>/";
$timeout = 2000;
$socket = pfsockopen($protocol . "://" . $host, $port,
$errno, $errstr, $timeout);
$content = "{'action': 'sendmessage', 'data': 'test'}";
$body = "POST $path HTTP/1.1
";
$body .= "Host: $host
";
$body .= "Content-Type: application/json
";
$body .= "Content-Length: " . strlen($content) . "
";
$body .= "Connection: Close
";
$body .= $content;
$body .= "
";
fwrite($socket, $body);
However, nothing happens.
If I use wscat, like:
wscat -c wss://<my-endpoint>.amazonaws.com/<my-stage>
> {'action': 'sendmessage', 'data': 'test'}
>
it works just fine.
What am I doing wrong in my php code?
Note: I need the socket connection to be persistent (the way it is when using the pfsockopen function).
Since you didn't provide a handpoint link, here is some notes, following own tests!
I guess the issue comes from the wss
part, php needs to retrieve the certificate first, so it can encrypt the data.
Your code should work just fine on a ws://
stream.
To connect to a regular ws://
stream, one can simply use fsockopen().
<?php
$fp = fsockopen("udp://echo.websocket.org", 13, $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr<br />
";
} else {
fwrite($fp, "
");
echo "Connected!";
echo fread($fp, 26);
fclose($fp);
}
But to connect to a wss://
secure websocket stream, using php, without libraries, we need to create a tunnel first, by querying the public key with stream_socket_client.
This is a handshake mechanism. This can be done as follow.
Notice the first ssl://
call. This is the TLS 1.0 protocol.
<?php
$sock = stream_socket_client("ssl://echo.websocket.org:443",$e,$n,30,STREAM_CLIENT_CONNECT,stream_context_create(null));
if(!$sock){
echo"[$n]$e".PHP_EOL;
} else {
fwrite($sock,"GET / HTTP/1.1
Host: echo.websocket.org
Accept: */*
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: ".rand(0,999)."
");
while(!feof($sock)){
var_dump(fgets($sock,2048));
}
}
The output should looks like:
string(44) "HTTP/1.1 101 Web Socket Protocol Handshake"
string(21) "Connection: Upgrade"
string(37) "Date: Thu, 12 Dec 2019 04:06:27 GMT"
string(52) "Sec-WebSocket-Accept: fTYwcEa6D9kJBtghptkz1e9CtBI="
string(25) "Server: Kaazing Gateway"
string(20) "Upgrade: websocket"
Same base code, another example, pulling data from Binance wss://
stream.
We can also use TLS 1.2, with a tls://
handshake instead. Works on most servers.
<?php
$sock = stream_socket_client("tls://stream.binance.com:9443",$error,$errnum,30,STREAM_CLIENT_CONNECT,stream_context_create(null));
if (!$sock) {
echo "[$errnum] $error" . PHP_EOL;
} else {
echo "Connected - Do NOT get rekt!" . PHP_EOL;
fwrite($sock, "GET /stream?streams=btcusdt@kline_1m HTTP/1.1
Host: stream.binance.com:9443
Accept: */*
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: ".rand(0,999)."
");
while (!feof($sock)) {
var_dump(explode(",",fgets($sock, 512)));
}
}
Here is a way to retrieve only the ssl RSA public key of a remote handpoint, from php. Can be used to speed up later connections.
<?php
$opt = [
"capture_peer_cert" => true,
"capture_peer_cert_chain" => true
];
$a = stream_context_create(["ssl"=>$opt]);
$b = stream_socket_client("ssl://stream.binance.com:9443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $a);
$cont = stream_context_get_params($b);
$key = openssl_pkey_get_public($cont["options"]["ssl"]["peer_certificate"]);
$c = openssl_pkey_get_details($key);
var_dump($c["key"]);
Output something like:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhki(...)7aEsFtUNkwM5R5b1mpqzAwqHyvdamJx20bT6SS6
PYXSr/dv8ak1d4e2Q0nIa1O7l3w0bZZ4wnp5B8Z+tjPd1W8uaZoRO2iVkPMh2yPl
j0mmtUw1YlfDyutH/t4FlRCDiD4JjdREQGs381/+jbkdjl2SIb1IyNiCdAXA6zsq
xwIDAQAB
-----END PUBLIC KEY-----
There is possibly other quircks, to be sure, we need the main handpoint^. Would be glad to test that. Otherwise good luck, there is a big lack of documentation on the subject.
This is still a new born protocol (2011!). Best details are in the RFC specification:
The WebSocket protocol was standardized by the IETF as RFC 6455 in 2011
About the handshake, it must be initiated by a GET request.
The client will send a pretty standard HTTP request with headers that looks like this (the HTTP version must be 1.1 or greater, and the method must be GET)
Writing_WebSocket_servers#Client_handshake_request
In short:
If unencrypted WebSocket traffic flows through an explicit or a transparent proxy server without WebSockets support, the connection will likely fail.
WebSocket#Proxy_traversal
Transport_Layer_Security#Digital_certificates
這篇關于WebSockets - 通過 php 發送 json 數據的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!