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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 15:38:39  来源:igfitidea点击:

PHP client Web socket to send messages

phplaravelcodeignitersocketstcp

提问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发布/订阅消息系统