laravel PHP 客户端 Web 套接字发送消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42955033/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP client Web socket to send messages
提问by iCoders
I have following code
我有以下代码
index.php
索引.php
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
.panel{
margin-right: 3px;
}
.button {
background-color: #4CAF50;
border: none;
color: white;
margin-right: 30%;
margin-left: 30%;
text-decoration: none;
display: block;
font-size: 16px;
cursor: pointer;
width:30%;
height:40px;
margin-top: 5px;
}
input[type=text]{
width:100%;
margin-top:5px;
}
.chat_wrapper {
width: 70%;
height:472px;
margin-right: auto;
margin-left: auto;
background: #3B5998;
border: 1px solid #999999;
padding: 10px;
font: 14px 'lucida grande',tahoma,verdana,arial,sans-serif;
}
.chat_wrapper .message_box {
background: #F7F7F7;
height:350px;
overflow: auto;
padding: 10px 10px 20px 10px;
border: 1px solid #999999;
}
.chat_wrapper input{
//padding: 2px 2px 2px 5px;
}
.system_msg{color: #BDBDBD;font-style: italic;}
.user_name{font-weight:bold;}
.user_message{color: #88B6E0;}
@media only screen and (max-width: 720px) {
/* For mobile phones: */
.chat_wrapper {
width: 95%;
height: 40%;
}
.button{ width:100%;
margin-right:auto;
margin-left:auto;
height:40px;}
}
</style>
</head>
<body>
<?php
$colours = array('007AFF','FF7000','FF7000','15E25F','CFC700','CFC700','CF1100','CF00BE','F00');
$user_colour = array_rand($colours);
?>
<script src="jquery-3.1.1.js"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function(){
//create a new WebSocket object.
var wsUri = "ws://localhost:9000/demo/server.php";
websocket = new WebSocket(wsUri);
websocket.onopen = function(ev) { // connection is open
$('#message_box').append("<div class=\"system_msg\">Connected!</div>"); //notify user
}
$('#send-btn').click(function(){ //use clicks message send button
var mymessage = $('#message').val(); //get message text
var myname = $('#name').val(); //get user name
if(myname == ""){ //empty name?
alert("Enter your Name please!");
return;
}
if(mymessage == ""){ //emtpy message?
alert("Enter Some message Please!");
return;
}
document.getElementById("name").style.visibility = "hidden";
var objDiv = document.getElementById("message_box");
objDiv.scrollTop = objDiv.scrollHeight;
//prepare json data
var msg = {
message: mymessage,
name: myname,
color : '<?php echo $colours[$user_colour]; ?>'
};
//convert and send data to server
websocket.send(JSON.stringify(msg));
});
//#### Message received from server?
websocket.onmessage = function(ev) {
var msg = JSON.parse(ev.data); //PHP sends Json data
var type = msg.type; //message type
var umsg = msg.message; //message text
var uname = msg.name; //user name
var ucolor = msg.color; //color
if(type == 'usermsg')
{
$('#message_box').append("<div><span class=\"user_name\" style=\"color:#"+ucolor+"\">"+uname+"</span> : <span class=\"user_message\">"+umsg+"</span></div>");
}
if(type == 'system')
{
$('#message_box').append("<div class=\"system_msg\">"+umsg+"</div>");
}
$('#message').val(''); //reset text
var objDiv = document.getElementById("message_box");
objDiv.scrollTop = objDiv.scrollHeight;
};
websocket.onerror = function(ev){$('#message_box').append("<div class=\"system_error\">Error Occurred - "+ev.data+"</div>");};
websocket.onclose = function(ev){$('#message_box').append("<div class=\"system_msg\">Connection Closed</div>");};
});
</script>
<div class="chat_wrapper">
<div class="message_box" id="message_box"></div>
<div class="panel">
<input type="text" name="name" id="name" placeholder="Your Name" maxlength="15" />
<input type="text" name="message" id="message" placeholder="Message" maxlength="80"
onkeydown = "if (event.keyCode == 13)document.getElementById('send-btn').click()" />
</div>
<button id="send-btn" class=button>Send</button>
</div>
</body>
</html>
server.php
服务器.php
<?php
$host = 'localhost'; //host
$port = '9000'; //port
$null = NULL; //null var
//Create TCP/IP sream socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
//reuseable port
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
//bind socket to specified host
socket_bind($socket, 0, $port);
//listen to port
socket_listen($socket);
//create & add listning socket to the list
$clients = array($socket);
//start endless loop, so that our script doesn't stop
while (true) {
//manage multipal connections
$changed = $clients;
//returns the socket resources in $changed array
socket_select($changed, $null, $null, 0, 10);
//check for new socket
if (in_array($socket, $changed)) {
$socket_new = socket_accept($socket); //accpet new socket
$clients[] = $socket_new; //add socket to client array
$header = socket_read($socket_new, 1024); //read data sent by the socket
perform_handshaking($header, $socket_new, $host, $port); //perform websocket handshake
socket_getpeername($socket_new, $ip); //get ip address of connected socket
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); //prepare json data
send_message($response); //notify all users about new connection
//make room for new socket
$found_socket = array_search($socket, $changed);
unset($changed[$found_socket]);
}
//loop through all connected sockets
foreach ($changed as $changed_socket) {
//check for any incomming data
while(socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$received_text = unmask($buf); //unmask data
$tst_msg = json_decode($received_text); //json decode
$user_name = $tst_msg->name; //sender name
$user_message = $tst_msg->message; //message text
$user_color = $tst_msg->color; //color
//prepare data to be sent to client
$response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color)));
send_message($response_text); //send data
break 2; //exist this loop
}
$buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ);
if ($buf === false) { // check disconnected client
// remove client for $clients array
$found_socket = array_search($changed_socket, $clients);
socket_getpeername($changed_socket, $ip);
unset($clients[$found_socket]);
//notify all users about disconnected connection
$response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected')));
send_message($response);
}
}
}
// close the listening socket
socket_close($socket);
function send_message($msg)
{
global $clients;
foreach($clients as $changed_socket)
{
@socket_write($changed_socket,$msg,strlen($msg));
}
return true;
}
//Unmask incoming framed message
function unmask($text) {
$length = ord($text[1]) & 127;
if($length == 126) {
$masks = substr($text, 4, 4);
$data = substr($text, 8);
}
elseif($length == 127) {
$masks = substr($text, 10, 4);
$data = substr($text, 14);
}
else {
$masks = substr($text, 2, 4);
$data = substr($text, 6);
}
$text = "";
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i%4];
}
return $text;
}
//Encode message for transfer to client.
function mask($text)
{
$b1 = 0x80 | (0x1 & 0x0f);
$length = strlen($text);
if($length <= 125)
$header = pack('CC', $b1, $length);
elseif($length > 125 && $length < 65536)
$header = pack('CCn', $b1, 126, $length);
elseif($length >= 65536)
$header = pack('CCNN', $b1, 127, $length);
return $header.$text;
}
//handshake new client.
function perform_handshaking($receved_header,$client_conn, $host, $port)
{
$headers = array();
$lines = preg_split("/\r\n/", $receved_header);
foreach($lines as $line)
{
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches))
{
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//hand shaking header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host\r\n" .
"WebSocket-Location: ws://$host:$port/demo/shout.php\r\n".
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_conn,$upgrade,strlen($upgrade));
}
The above code is working fine.But now i am looking for client side as php .can any provide me some example to make client websocket using php
上面的代码工作正常。但现在我正在寻找客户端作为 php 。可以给我提供一些使用 php 制作客户端 websocket 的示例
UPDATES
更新
I need to send byte array using php or javascript library through websocket
我需要通过 websocket 使用 php 或 javascript 库发送字节数组
Update 2I have found simple class of websocket but dont know how to use
更新 2我找到了简单的 websocket 类,但不知道如何使用
https://github.com/paragi/PHP-websocket-client/blob/master/websocket_client.php
https://github.com/paragi/PHP-websocket-client/blob/master/websocket_client.php
Also as per Niket Pathak answer i tried but getting following error
同样根据 Niket Pathak 的回答,我尝试过但出现以下错误
<?php
$host = 'ws://echo.websocket.org'; // your websocket server
$port = 80;
$local = "http://localhost"; // url where this script run | Client
$data = '{"id": 2,"command": "server_info"}'; // data to be sent
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Sec-WebSocket-Version: 13"."\r\n".
"Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
// WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000);
fclose($sock);
var_dump(hybi10Decode($wsdata));
// hibi10 decoding of data
function hybi10Decode($data)
{
$bytes = $data;
$dataLength = '';
$mask = '';
$coded_data = '';
$decodedData = '';
$secondByte = sprintf('%08b', ord($bytes[1]));
$masked = ($secondByte[0] == '1') ? true : false;
$dataLength = ($masked === true) ? ord($bytes[1]) & 127 : ord($bytes[1]);
if($masked === true)
{
if ($dataLength === 126) {
$mask = substr($bytes, 4, 4);
$coded_data = substr($bytes, 8);
}
elseif ($dataLength === 127) {
$mask = substr($bytes, 10, 4);
$coded_data = substr($bytes, 14);
}
else {
$mask = substr($bytes, 2, 4);
$coded_data = substr($bytes, 6);
}
for ($i = 0; $i < strlen($coded_data); $i++) {
$decodedData .= $coded_data[$i] ^ $mask[$i % 4];
}
}
else {
if ($dataLength === 126) {
$decodedData = substr($bytes, 4);
}
elseif ($dataLength === 127) {
$decodedData = substr($bytes, 10);
}
else {
$decodedData = substr($bytes, 2);
}
}
return $decodedData;
}
// hibi10 encoding of data
function hybi10Encode($payload, $type = 'text', $masked = true) {
$frameHead = array();
$frame = '';
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;
case 'close':
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;
case 'ping':
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;
case 'pong':
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}
// set mask and payload length (using 1, 3 or 9 bytes)
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]);
}
// most significant bit MUST be 0 (close connection if frame too big)
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;
}
// convert frame-head to string:
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
// generate a random mask:
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
// append payload to frame:
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
Error
错误
Warning: fsockopen(): unable to connect to ws://echo.websocket.org:80 (Unable to find the socket transport "ws" - did you forget to enable it when you configured PHP?) in G:\XAMPP\htdocs\a\server.php on line 16
Warning: fwrite() expects parameter 1 to be resource, boolean given in G:\XAMPP\htdocs\a\server.php on line 17 error:407024:Unable to find the socket transport "ws" - did you forget to enable it when you configured PHP?
警告:fsockopen():无法连接到 ws://echo.websocket.org:80(无法找到套接字传输“ws” - 您在配置 PHP 时是否忘记启用它?)在 G:\XAMPP\ htdocs\a\server.php 第 16 行
警告:fwrite() 期望参数 1 是资源,布尔值在 G:\XAMPP\htdocs\a\server.php 第 17 行错误:407024:无法找到套接字传输“ws”-您是否忘记启用它当你配置 PHP 时?
采纳答案by Niket Pathak
If you are talking about connecting to your Websocket using Php, then YES, it is doable. There are a few libraries like Php WebSocket clientor Websocket phpthat can connect to your websocket as a Client.
如果您正在谈论使用 Php 连接到您的 Websocket,那么YES,它是可行的。有一些库,如PHP WebSocket 客户端或Websocket php,可以作为客户端连接到您的 websocket。
You can also check out this Websocket Clientfor an example.
您还可以查看此Websocket 客户端作为示例。
Another Working Examplewithout using any library and implementing the hybi10 frame encodingas per the specification:
另一个不使用任何库并按照规范实现 hybi10 帧编码的工作示例:
$host = 'www.host.com'; // your websocket server
$port = 443;
$local = "http://localhost"; // url where this script runs | Client
$data = '{"id": 2,"command": "server_info"}'; // data to be sent
$head = "GET / HTTP/1.1"."\r\n".
"Upgrade: WebSocket"."\r\n".
"Connection: Upgrade"."\r\n".
"Origin: $local"."\r\n".
"Host: $host"."\r\n".
"Sec-WebSocket-Version: 13"."\r\n".
"Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n".
"Content-Length: ".strlen($data)."\r\n"."\r\n";
// WebSocket handshake
$sock = fsockopen($host, $port, $errno, $errstr, 2);
fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr);
$headers = fread($sock, 2000);
fwrite($sock, hybi10Encode($data)) or die('error:'.$errno.':'.$errstr);
$wsdata = fread($sock, 2000);
fclose($sock);
var_dump(hybi10Decode($wsdata));
// hibi10 decoding of data
function hybi10Decode($data)
{
$bytes = $data;
$dataLength = '';
$mask = '';
$coded_data = '';
$decodedData = '';
$secondByte = sprintf('%08b', ord($bytes[1]));
$masked = ($secondByte[0] == '1') ? true : false;
$dataLength = ($masked === true) ? ord($bytes[1]) & 127 : ord($bytes[1]);
if($masked === true)
{
if ($dataLength === 126) {
$mask = substr($bytes, 4, 4);
$coded_data = substr($bytes, 8);
}
elseif ($dataLength === 127) {
$mask = substr($bytes, 10, 4);
$coded_data = substr($bytes, 14);
}
else {
$mask = substr($bytes, 2, 4);
$coded_data = substr($bytes, 6);
}
for ($i = 0; $i < strlen($coded_data); $i++) {
$decodedData .= $coded_data[$i] ^ $mask[$i % 4];
}
}
else {
if ($dataLength === 126) {
$decodedData = substr($bytes, 4);
}
elseif ($dataLength === 127) {
$decodedData = substr($bytes, 10);
}
else {
$decodedData = substr($bytes, 2);
}
}
return $decodedData;
}
// hibi10 encoding of data
function hybi10Encode($payload, $type = 'text', $masked = true) {
$frameHead = array();
$frame = '';
$payloadLength = strlen($payload);
switch ($type) {
case 'text':
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;
case 'close':
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;
case 'ping':
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;
case 'pong':
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}
// set mask and payload length (using 1, 3 or 9 bytes)
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]);
}
// most significant bit MUST be 0 (close connection if frame too big)
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;
}
// convert frame-head to string:
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
// generate a random mask:
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}
$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);
// append payload to frame:
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}
return $frame;
}
Lastly, you can send your byte data as a JSON object by encoding it to base64 first. Check this SO Postto see how to do that. Hope this helps.
最后,您可以首先将字节数据编码为 base64,将其作为 JSON 对象发送。检查此SO Post以了解如何执行此操作。希望这可以帮助。
回答by Nutscracker
My unswer NO. You can not fully use the websocket client logick, using only the server part(for example php). Because the server side know anything about the browser. Server - receives a request and returns a static result. All magic in the browser is javascript. Javascript can dynamically listen all user events, post a query to server and insert a response from the server to the page. Websocket can not work without javascript in browser. This is WebSocket definition:
我不知道没有。您不能完全使用 websocket 客户端逻辑,仅使用服务器部分(例如 php)。因为服务器端对浏览器一无所知。服务器 - 接收请求并返回静态结果。浏览器中的所有魔法都是 javascript。Javascript 可以动态侦听所有用户事件,向服务器发布查询并将服务器响应插入页面。如果浏览器中没有 javascript,Websocket 将无法工作。这是 WebSocket 定义:
WebSockets are a bi-directional, full-duplex, persistent connection from a web browser to a server. Once a WebSocket connection is established the connection stays open until the client or server decides to close this connection. With this open connection, the client or server can send a message at any given time to the other. This makes web programming entirely event driven, not (just) user initiated. It is stateful. As well, at this time, a single running server application is aware of all connections, allowing you to communicate with any number of open connections at any given time.
WebSocket 是一种从 Web 浏览器到服务器的双向、全双工、持久连接。一旦建立了 WebSocket 连接,该连接将保持打开状态,直到客户端或服务器决定关闭此连接。有了这个打开的连接,客户端或服务器可以在任何给定的时间向另一个发送消息。这使得 Web 编程完全是事件驱动的,而不是(只是)用户启动的。它是有状态的。同样,此时,单个运行的服务器应用程序知道所有连接,允许您在任何给定时间与任意数量的打开连接进行通信。
The only thing you can do, is learn node js. It will allow you to write the browser (client) part and server part on one language - javascript. But however this will be different parts of application, and you will use different approaches, to program each part
你唯一能做的就是学习 node js。它将允许您使用一种语言 - javascript 编写浏览器(客户端)部分和服务器部分。但是,这将是应用程序的不同部分,您将使用不同的方法来对每个部分进行编程
回答by Maximiliano Nunes Catarino
It's not what you want but could help. You can use Redis as a middleware. Use Redis publish/subscribe message system. Redis publish/subscribe message system
这不是您想要的,但可以提供帮助。您可以使用 Redis 作为中间件。使用Redis发布/订阅消息系统。 Redis发布/订阅消息系统