我正在尝试从PHP向我的websocket aws api网关发送即弃请求。
我已经设置了一个名为“ sendmessage”的动作。
这是我正在使用的代码:
$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\r\n";
$body .= "Host: $host\r\n";
$body .= "Content-Type: application/json\r\n";
$body .= "Content-Length: " . strlen($content) . "\r\n";
$body .= "Connection: Close\r\n\r\n";
$body .= $content;
$body .= "\r\n";
fwrite($socket, $body);
但是什么也没发生。
如果我使用wscat,例如:
wscat -c wss://<my-endpoint>.amazonaws.com/<my-stage>
> {'action': 'sendmessage', 'data': 'test'}
>
它工作正常。
我的php代码在做什么错了?
注意:我需要套接字连接是持久的(使用pfsockopen函数时的方式)。
答案 0 :(得分:4)
由于您未提供指向链接,因此请注意以下内容,并请自己进行测试!
我猜问题出在wss
部分,php需要首先检索证书,以便它可以加密数据。
您的代码在ws://
流上应该可以正常工作。
要连接到常规ws://
流,只需使用fsockopen()。
<?php
$fp = fsockopen("udp://echo.websocket.org", 13, $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr<br />\n";
} else {
fwrite($fp, "\n");
echo "Connected!";
echo fread($fp, 26);
fclose($fp);
}
但是要使用不带库的php连接到wss://
安全websocket 流,我们需要先通过查询公钥创建一条隧道与stream_socket_client。
这是一种握手机制。可以按照以下步骤进行。
请注意第一个ssl://
呼叫。这是TLS 1.0协议。
<?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\r\nHost: echo.websocket.org\r\nAccept: */*\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: ".rand(0,999)."\r\n\r\n");
while(!feof($sock)){
var_dump(fgets($sock,2048));
}
}
输出应如下所示:
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"
另一个示例是相同的基本代码,它从Binance wss://
流中提取数据。
我们也可以使用TLS 1.2,而使用tls://
握手。在大多数服务器上均可使用。
<?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\r\nHost: stream.binance.com:9443\r\nAccept: */*\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: ".rand(0,999)."\r\n\r\n");
while (!feof($sock)) {
var_dump(explode(",",fgets($sock, 512)));
}
}
这是从php中仅检索远程点的 ssl RSA公钥的一种方法。可用于加快以后的连接速度。
<?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"]);
输出类似:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhki(...)7aEsFtUNkwM5R5b1mpqzAwqHyvdamJx20bT6SS6
PYXSr/dv8ak1d4e2Q0nIa1O7l3w0bZZ4wnp5B8Z+tjPd1W8uaZoRO2iVkPMh2yPl
j0mmtUw1YlfDyutH/t4FlRCDiD4JjdREQGs381/+jbkdjl2SIb1IyNiCdAXA6zsq
xwIDAQAB
-----END PUBLIC KEY-----
当然,可能还有其他问题,我们需要主要观点^。很乐意对此进行测试。否则,祝你好运,有关此主题的文档很多。
这是still a new born protocol(2011年!)。最好的细节在RFC规范中:
在IETF中,WebSocket协议被IETF标准化为RFC 6455 2011
关于握手,它必须由GET请求启动。
客户端将发送带有标题的标准HTTP请求 看起来像这样(HTTP版本必须为1.1或更高版本,并且 方法必须是GET)
Writing_WebSocket_servers#Client_handshake_request
简而言之:
如果未加密的WebSocket流量通过显式或 没有WebSockets支持的透明代理服务器,连接 可能会失败。
答案 1 :(得分:0)
您需要根据需要使用PHP Websocket客户端。以下是可用于您的需求的客户端之一:
https://github.com/paragi/PHP-websocket-client
示例1:
if( $sp = websocket_open('echo.websocket.org',80) ) {
websocket_write($sp,"hello server");
echo "Server responed with: " . websocket_read($sp,$errstr);
}
示例2:
$headers = ["Cookie: SID=".session_id()];
$sp = websocket_open('echo.websocket.org',80,$headers,$errstr,16);
if($sp){
$bytes_written = websocket_write($sp,"hello server");
if($bytes_written){
$data = websocket_read($sp,$errstr);
echo "Server responed with: " . $errstr ? $errstr : $data;
}
}
希望这会有所帮助。
答案 2 :(得分:0)
您可能需要使用http_build_query();
函数,例如:
$content = http_build_query($content);
并使用表单发布来发送消息,因此请尝试以下代码来检查套接字连接是否成功,可能您的代码中的错误是pfsockopen()
应该是@fsockopen()
根据您的要求进行编辑:
$protocol = "ssl";
$host = "<myendpoint>.amazonaws.com";
$port = 443;
$path = "/<mystage>/";
$timeout = 2000;
$socket = @fsockopen($protocol . "://" . $host, $port,
$errno, $errstr, $timeout);
if($socket === false) { return false; };
$content = "{'action': 'sendmessage', 'data': 'test'}";
$body = "POST $path HTTP/1.1\r\n";
$body .= "Host: $host\r\n";
$body .= "Referer: yourClass (v.".version() .")\r\n";
$body .= "Content-type: application/json\r\n";
$body .= "Content-Length: ".strlen($content)."\r\n";
$body .= "Connection: Close\r\n\r\n";
$body .= "$content";
fwrite($socket, $body);
fclose($socket);
此代码在我的网站上可以作为
的功能正常运行$ out。=“内容类型:application / x-www-form-urlencoded \ r \ n”;而不是json
function flexy_Request($url, $_data) {
// parse the given URL
$url = parse_url($url);
if ($url === false || !isset($url['host']) || !isset($url['path'])) {
return false;
}
// extract host and path:
$host = $url['host'];
$path = $url['path'];
// open a socket connection on port 80
// use localhost in case of issues with NATs (hairpinning)
$fp = @fsockopen($host, 80);
if($fp===false) { return false; };
$data = http_build_query($_data);
$out = "POST $path HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Referer: Myclass (v.". flexy_version() .")\r\n";
$out .= "Content-type: application/json\r\n";
$out .= "Content-Length: ".strlen($data)."\r\n";
$out .= "Connection: Close\r\n\r\n";
$out .= "$data";
$number_bytes_sent = fwrite($fp, $out);
fclose($fp);
return $number_bytes_sent; // or false on fwrite() error
}
答案 3 :(得分:-1)
我建议对PHP使用cURL扩展名。默认情况下,cURL库维护一个持久连接池。
<?php
$proto = 'https';
$host = 'FQDN';
$path = '/path/to/file';
$post_data_array = array(
'action' => 'sendmessage',
'data' => 'test',
);
$payload = json_encode($post_data_array);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $proto.'://'.$host.$path);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($payload),
'Connection: Keep-Alive',
'Keep-Alive: 300',
)
);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);