php實(shí)現(xiàn)socket即時(shí)通訊示例

在socket出現(xiàn)之前已經(jīng)有ajax定時(shí)請求、長輪詢等方案,但都不能滿足某些特定情況下的需求,基于此,socket就應(yīng)運(yùn)而生了。

總結(jié)下常用的socket函數(shù)
服務(wù)端: socket_create 創(chuàng)建socket設(shè)置基本參數(shù)
     socket_bind 綁定ip和端口號
     socket_listen 監(jiān)聽
     socket_accept 客戶端的連接
     socket_read 讀取客戶端的數(shù)據(jù)
     socket_write 給單獨(dú)客戶端發(fā)送數(shù)據(jù)
     socket_close 關(guān)閉連接

客戶端:socket_create 創(chuàng)建socket設(shè)置基本參數(shù)
    socket_connect 連接socket
    socket_write 給服務(wù)端發(fā)送數(shù)據(jù)
    socket_read 讀取服務(wù)端數(shù)據(jù)
    socket_close 關(guān)閉連接

服務(wù)端代碼socket.php(名字自己定義):

<?php
class WS {
   var $master;
   var $sockets = array();
   var $debug = false;//true為調(diào)試模式,輸出log日志
   var $handshake = array();

   function __construct($address, $port){
       $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
       socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
       socket_bind($this->master, $address, $port)  or die("socket_bind() failed");
       socket_listen($this->master,20)  or die("socket_listen() failed");
        
       $this->sockets[] = $this->master;
       $this->say("Server Started : ".date('Y-m-d H:i:s'));
       $this->say("Listening on : ".$address." port ".$port);
       $this->say("Master socket : ".$this->master."\n");
        
       while(true){
           $socketArr = $this->sockets;
           $write = NULL;
           $except = NULL;
           socket_select($socketArr, $write, $except, NULL); //自動(dòng)選擇來消息的socket 如果是握手 自動(dòng)選擇主機(jī)
           foreach ($socketArr as $socket){
               if ($socket == $this->master){ //主機(jī)
                    $client = socket_accept($this->master);
                    if ($client < 0){
                        $this->log("socket_accept() failed");
                        continue;
                    } else{
                        $this->connect($client);
                    }
               } else {
                    $bytes = @socket_recv($socket,$buffer,2048,0);
                    if ($bytes == 0){
                        $this->disConnect($socket);
                    }
                    else{
                        $key = array_search($socket, $this->sockets);
                        if (empty($this->handshake) || !isset($this->handshake[$key]) || !$this->handshake[$key]){
                            $this->doHandShake($socket, $buffer, $key);
                        }
                        else{
                            $buffer = $this->decode($buffer);
                            echo $buffer.PHP_EOL;
                            $key = array_search($socket, $this->sockets);
                            $arr = $this->sockets;
                            array_shift($arr);
                            foreach ($arr as $s){
                                $this->send($s, $buffer);
                            }
                        }
                    }
               }
           }
       }
   }
    

   function send($client, $msg){
       // $msg = $this->frame($msg);
       $msg = $this->frame('恭喜你,成功了');
       socket_write($client, $msg, strlen($msg));
   }


   function connect($socket){
       array_push($this->sockets, $socket);
       $this->say("\n" . $socket . " CONNECTED!");
       $this->say(date("Y-n-d H:i:s"));
   }


   function disConnect($socket){
       $index = array_search($socket, $this->sockets);
       socket_close($socket);
       $this->say($socket . " DISCONNECTED!");
       if ($index >= 0){
           echo 'unset index is:'.PHP_EOL;
           unset($this->sockets[$index]);
       }
   }


   function doHandShake($socket, $buffer, $handKey){
       $this->log("\nRequesting handshake...");
       $this->log($buffer);
       list($resource, $host, $origin, $key) = $this->getHeaders($buffer);
       $this->log("Handshaking...");
       $upgrade = "HTTP/1.1 101 Switching Protocol\r\n" .
        "Upgrade: websocket\r\n" .
        "Connection: Upgrade\r\n" .
        "Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n"; //必須以兩個(gè)回車結(jié)尾
       $this->log($upgrade);
       $sent = socket_write($socket, $upgrade, strlen($upgrade));
       $this->handshake[$handKey]=true;
       $this->log("Done handshaking...");
       return true;
   }


   function getHeaders($req){
       $r = $h = $o = $key = null;
       if (preg_match("/GET (.*) HTTP/" ,$req,$match)) { $r = $match[1]; }
       if (preg_match("/Host: (.*)\r\n/" ,$req,$match)) { $h = $match[1]; }
       if (preg_match("/Origin: (.*)\r\n/" ,$req,$match)) { $o = $match[1]; }
       if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)) { $key = $match[1]; }
       return array($r, $h, $o, $key);
   }


   function calcKey($key){
       //基于websocket version 13
       $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
       return $accept;
   }


   function decode($buffer) {
       $len = $masks = $data = $decoded = null;
       $len = ord($buffer[1]) & 127;
       if ($len === 126) {
           $masks = substr($buffer, 4, 4);
           $data = substr($buffer, 8);
       } 
       else if ($len === 127) {
           $masks = substr($buffer, 10, 4);
           $data = substr($buffer, 14);
       } 
       else {
           $masks = substr($buffer, 2, 4);
           $data = substr($buffer, 6);
       }
       for ($index = 0; $index < strlen($data); $index++) {
          $decoded .= $data[$index] ^ $masks[$index % 4];
       }
       return $decoded;
   }


   function frame($s){
       $a = str_split($s, 125);
       if (count($a) == 1){
          return "\x81" . chr(strlen($a[0])) . $a[0];
       }
       $ns = "";
       foreach ($a as $o){
          $ns .= "\x81" . chr(strlen($o)) . $o;
       }
       return $ns;
   }
    
   function say($msg = ""){
      echo $msg . "\n";
   }


   function log($msg = ""){
       if ($this->debug){
          echo $msg . "\n";
       } 
   }
}
  
new WS('localhost', 4000);

客戶端代碼index.html(H5):

<html>
     <head>
         <title>demo</title>
         <script src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
     </head>
     <body>
         <input type="text" id="content">
         <input type="button" value="send" id="send">
         <script type="text/javascript">
          var ws = new WebSocket("ws://localhost:4000");

          ws.onopen = function(){
                console.log("握手成功");
          }

          ws.onmessage = function(e){
                console.log("message:" + e.data);
          }

          ws.onerror = function(){
                console.log("error");
          }

          $("#send").click(function(){
                  content = $("#content").val();
                  console.log(content);
                  ws.send(content);
          })
         </script>
     </body>
</html>

然后到socket.php所在的目錄執(zhí)行php socket.php 開啟socket(直接使用php命令要添加環(huán)境變量),linux下執(zhí)行nohup php demo.php &可以在后臺(tái)執(zhí)行,瀏覽器打開多個(gè)index.html,就能建立通訊了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評論 19 139
  • Java Socket編程 對于Java Socket編程而言,有兩個(gè)概念,一個(gè)是ServerSocket,一個(gè)是...
    天空下天的月亮閱讀 2,131評論 10 41
  • 對TCP/IP、UDP、Socket編程這些詞你不會(huì)很陌生吧?隨著網(wǎng)絡(luò)技術(shù)的發(fā)展,這些詞充斥著我們的耳朵。那么我想...
    yuantao123434閱讀 5,527評論 1 97
  • 第一章 Nginx簡介 Nginx是什么 沒有聽過Nginx?那么一定聽過它的“同行”Apache吧!Ngi...
    JokerW閱讀 33,042評論 24 1,002
  • CSS三大特性—— 繼承、 優(yōu)先級和層疊。 1.CSS選擇器分類 全局選擇器(通配符*) 標(biāo)簽選擇器(body,d...
    肆意咯咯咯閱讀 5,230評論 0 1

友情鏈接更多精彩內(nèi)容