• Websocket和PHP Socket编程


    本来是搜一些html5 websocket资料看的,结果被引去看了php的socket编程。下面是一些简单的例子,在命令行运行php脚本就行

    [命令行运行PHP]PHP中有一个php.exe文件,可以用命令执行PHP脚本。如:D:/php.exe -f F:/test.php ; 可以使用php.exe -h查看更多参数 :

    server端:

    [php] view plain copy
     
    1. <?php  
    2. /** 
    3.  * 服务器端代码 
    4.  * 
    5.  */  
    6. //确保在连接客户端时不会超时  
    7. set_time_limit(0);  
    8. //设置IP和端口号  
    9. $address = "localhost";  
    10. $port = 1234; //调试的时候,可以多换端口来测试程序!  
    11. //创建一个SOCKET  
    12. if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)  
    13. {  
    14.     echo "socket_create() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n";  
    15.     die;  
    16. }  
    17. //阻塞模式  
    18. if (socket_set_block($sock) == false)  
    19. {  
    20.     echo "socket_set_block() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n";  
    21.     die;  
    22. }  
    23. //绑定到socket端口  
    24. if (socket_bind($sock, $address, $port) == false)  
    25. {  
    26.     echo "socket_bind() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n";  
    27.     die;  
    28. }  
    29. //开始监听  
    30. if (socket_listen($sock, 4) == false)  
    31. {  
    32.     echo "socket_listen() 失败的原因是:" . socket_strerror(socket_last_error()) . "/n";  
    33.     die;  
    34. }  
    35. do  
    36. {  
    37.     if (($msgsock = socket_accept($sock)) === false)  
    38.     {  
    39.         echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error()) . "/n";  
    40.         die;  
    41.     }  
    42.     //发到客户端  
    43.     $msg = "welcome /n";  
    44.     if (socket_write($msgsock, $msg, strlen($msg)) === false)  
    45.     {  
    46.         echo "socket_write() failed: reason: " . socket_strerror(socket_last_error()) ."/n";  
    47.         die;  
    48.     }  
    49.     echo "读取客户端发来的信息/n";  
    50.     $buf = socket_read($msgsock, 8192);  
    51.     echo "收到的信息: $buf   /n";  
    52.      
    53.     socket_close($msgsock);  
    54. while (true);  
    55. socket_close($sock);  
    56. ?>  

    client端:

    [php] view plain copy
     
    1. <?php  
    2. /** 
    3.  * 客户端代码 
    4.  */  
    5.    
    6. error_reporting(0);  
    7. set_time_limit(0);  
    8. echo " TCP/IP Connection /n";  
    9. $service_port = 10001;  
    10. $address = '127.0.0.1';  
    11. $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);  
    12. if ($socket === false)  
    13. {  
    14.     die;  
    15. }  
    16. else  
    17. {  
    18.     echo "OK";  
    19. }  
    20. echo "试图连接 ";  
    21. if (socket_connect($socket, $address, $service_port) == false)  
    22. {  
    23.     $error = socket_strerror(socket_last_error());  
    24.     echo "socket_connect() failed./n","Reason: {$error} /n";  
    25.     die;  
    26. }  
    27. else  
    28. {  
    29.     echo "连接OK/n";  
    30. }  
    31. $in   = "Hello World/r/n";  
    32. if (socket_write($socket, $in, strlen($in)) === false)  
    33. {  
    34.     echo "socket_write() failed: reason: " . socket_strerror(socket_last_error()) ."/n";  
    35.     die;  
    36. }  
    37. else  
    38. {  
    39.     echo "发送到服务器信息成功!/n","发送的内容为: $in  /n";  
    40. }  
    41. $out  = "";  
    42. while ($out = socket_read($socket, 8192))  
    43. {  
    44.     echo "接受的内容为: ".$out;  
    45. }  
    46. echo "关闭SOCKET…/n";  
    47. socket_close($socket);  
    48. echo "关闭OK/n";  
    49. ?>  

    再看websocket协议,是HTTP协议升级来的。看其消息头:

    所以server端需要解析一下,并返回握手的协议内容:

    在网上找到解析的相关代码 phpwebsocket - url:   http://code.google.com/p/phpwebsocket/

    [php] view plain copy
     
    1. // Usage: $master=new WebSocket("localhost",12345);  
    2. class WebSocket{  
    3.   var $master;  
    4.   var $sockets = array();  
    5.   var $users   = array();  
    6.   var $debug   = false;  
    7.    
    8.   function __construct($address,$port){  
    9.     error_reporting(E_ALL);  
    10.     set_time_limit(0);  
    11.     ob_implicit_flush();  
    12.     $this->master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");  
    13.     socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");  
    14.     socket_bind($this->master, $address, $port)                    or die("socket_bind() failed");  
    15.     socket_listen($this->master,20)                                or die("socket_listen() failed");  
    16.     $this->sockets[] = $this->master;  
    17.     $this->say("Server Started : ".date('Y-m-d H:i:s'));  
    18.     $this->say("Listening on   : ".$address." port ".$port);  
    19.     $this->say("Master socket  : ".$this->master."/n");  
    20.     while(true){  
    21.       $changed = $this->sockets;  
    22.       socket_select($changed,$write=NULL,$except=NULL,NULL);  
    23.       foreach($changed as $socket){  
    24.         if($socket==$this->master){  
    25.           $client=socket_accept($this->master);  
    26.           if($client<0){ $this->log("socket_accept() failed"); continue; }  
    27.           else{ $this->connect($client); }  
    28.         }  
    29.         else{  
    30.           $bytes = @socket_recv($socket,$buffer,2048,0);  
    31.           if($bytes==0){ $this->disconnect($socket); }  
    32.           else{  
    33.             $user = $this->getuserbysocket($socket);  
    34.             if(!$user->handshake){ $this->dohandshake($user,$buffer); }  
    35.             else{ $this->process($user,$this->unwrap($buffer)); }  
    36.           }  
    37.         }  
    38.       }  
    39.     }  
    40.   }  
    41.   function process($user,$msg){  
    42.     /* Extend and modify this method to suit your needs */  
    43.     /* Basic usage is to echo incoming messages back to client */  
    44.     $this->send($user->socket,$msg);  
    45.   }  
    46.   function send($client,$msg){  
    47.     $this->say("> ".$msg);  
    48.     $msg = $this->wrap($msg);  
    49.     socket_write($client,$msg,strlen($msg));  
    50.     $this->say("! ".strlen($msg));  
    51.   }  
    52.   function connect($socket){  
    53.     $user = new User();  
    54.     $user->id = uniqid();  
    55.     $user->socket = $socket;  
    56.     array_push($this->users,$user);  
    57.     array_push($this->sockets,$socket);  
    58.     $this->log($socket." CONNECTED!");  
    59.     $this->log(date("d/n/Y ")."at ".date("H:i:s T"));  
    60.   }  
    61.   function disconnect($socket){  
    62.     $found=null;  
    63.     $n=count($this->users);  
    64.     for($i=0;$i<$n;$i++){  
    65.       if($this->users[$i]->socket==$socket){ $found=$i; break; }  
    66.     }  
    67.     if(!is_null($found)){ array_splice($this->users,$found,1); }  
    68.     $index=array_search($socket,$this->sockets);  
    69.     socket_close($socket);  
    70.     $this->log($socket." DISCONNECTED!");  
    71.     if($index>=0){ array_splice($this->sockets,$index,1); }  
    72.   }  
    73.   function dohandshake($user,$buffer){  
    74.     $this->log("/nRequesting handshake...");  
    75.     $this->log($buffer);  
    76.     list($resource,$host,$origin,$key1,$key2,$l8b) = $this->getheaders($buffer);  
    77.     $this->log("Handshaking...");  
    78.     //$port = explode(":",$host);  
    79.     //$port = $port[1];  
    80.     //$this->log($origin."/r/n".$host);  
    81.     $upgrade  = "HTTP/1.1 101 WebSocket Protocol Handshake/r/n" .  
    82.                 "Upgrade: WebSocket/r/n" .  
    83.                 "Connection: Upgrade/r/n" .  
    84.                                 //"WebSocket-Origin: " . $origin . "/r/n" .  
    85.                                 //"WebSocket-Location: ws://" . $host . $resource . "/r/n" .  
    86.                 "Sec-WebSocket-Origin: " . $origin . "/r/n" .  
    87.                     "Sec-WebSocket-Location: ws://" . $host . $resource . "/r/n" .  
    88.                     //"Sec-WebSocket-Protocol: icbmgame/r/n" . //Client doesn't send this  
    89.                 "/r/n" .  
    90.                     $this->calcKey($key1,$key2,$l8b) . "/r/n";// .  
    91.                         //"/r/n";  
    92.     socket_write($user->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));  
    93.     $user->handshake=true;  
    94.     $this->log($upgrade);  
    95.     $this->log("Done handshaking...");  
    96.     return true;  
    97.   }  
    98.    
    99.   function calcKey($key1,$key2,$l8b){  
    100.         //Get the numbers  
    101.         preg_match_all('/([/d]+)/', $key1, $key1_num);  
    102.         preg_match_all('/([/d]+)/', $key2, $key2_num);  
    103.         //Number crunching [/bad pun]  
    104.         $this->log("Key1: " . $key1_num = implode($key1_num[0]) );  
    105.         $this->log("Key2: " . $key2_num = implode($key2_num[0]) );  
    106.         //Count spaces  
    107.         preg_match_all('/([ ]+)/', $key1, $key1_spc);  
    108.         preg_match_all('/([ ]+)/', $key2, $key2_spc);  
    109.         //How many spaces did it find?  
    110.         $this->log("Key1 Spaces: " . $key1_spc = strlen(implode($key1_spc[0])) );  
    111.         $this->log("Key2 Spaces: " . $key2_spc = strlen(implode($key2_spc[0])) );  
    112.         if($key1_spc==0|$key2_spc==0){ $this->log("Invalid key");return; }  
    113.         //Some math  
    114.         $key1_sec = pack("N",$key1_num / $key1_spc); //Get the 32bit secret key, minus the other thing  
    115.         $key2_sec = pack("N",$key2_num / $key2_spc);  
    116.         //This needs checking, I'm not completely sure it should be a binary string  
    117.         return md5($key1_sec.$key2_sec.$l8b,1); //The result, I think  
    118.   }  
    119.    
    120.   function getheaders($req){  
    121.     $r=$h=$o=null;  
    122.     if(preg_match("/GET (.*) HTTP/"               ,$req,$match)){ $r=$match[1]; }  
    123.     if(preg_match("/Host: (.*)/r/n/"              ,$req,$match)){ $h=$match[1]; }  
    124.     if(preg_match("/Origin: (.*)/r/n/"            ,$req,$match)){ $o=$match[1]; }  
    125.     if(preg_match("/Sec-WebSocket-Key1: (.*)/r/n/",$req,$match)){ $this->log("Sec Key1: ".$sk1=$match[1]); }  
    126.     if(preg_match("/Sec-WebSocket-Key2: (.*)/r/n/",$req,$match)){ $this->log("Sec Key2: ".$sk2=$match[1]); }  
    127.     if($match=substr($req,-8))                                                                  { $this->log("Last 8 bytes: ".$l8b=$match); }  
    128.     return array($r,$h,$o,$sk1,$sk2,$l8b);  
    129.   }  
    130.   function getuserbysocket($socket){  
    131.     $found=null;  
    132.     foreach($this->users as $user){  
    133.       if($user->socket==$socket){ $found=$user; break; }  
    134.     }  
    135.     return $found;  
    136.   }  
    137.   function     say($msg=""){ echo $msg."/n"; }  
    138.   function     log($msg=""){ if($this->debug){ echo $msg."/n"; } }  
    139.   function    wrap($msg=""){ return chr(0).$msg.chr(255); }  
    140.   function  unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }  
    141. }  
    142. class User{  
    143.   var $id;  
    144.   var $socket;  
    145.   var $handshake;  
    146. }  

    继承类:可以自己按需写,这里我添加了几行代码,sendAll()等,很方便就改成了一个即时的网页版聊天室。

    [php] view plain copy
     
    1. // Run from command prompt > php -q chatbot.demo.php  
    2. include "websocket.class.php";  
    3. // Extended basic WebSocket as ChatBot  
    4. class ChatBot extends WebSocket{  
    5.     function process($user,$msg){  
    6.           
    7.         if (isset($user->first)) {  
    8.             $this->send($user->socket,'');  
    9.             $user->first = true;  
    10.         }   
    11.           
    12.         $this->say("< ".$msg);  
    13.         switch($msg){  
    14.           case "hello" : $this->send($user->socket,"hello human");                       break;  
    15.           case "hi"    : $this->send($user->socket,"zup human");                         break;  
    16.           case "name"  : $this->send($user->socket,"my name is Multivac, silly I know"); break;  
    17.           case "age"   : $this->send($user->socket,"I am older than time itself");       break;  
    18.           case "date"  : $this->send($user->socket,"today is ".date("Y.m.d"));           break;  
    19.           case "time"  : $this->send($user->socket,"server time is ".date("H:i:s"));     break;  
    20.           case "thanks": $this->send($user->socket,"you're welcome");                    break;  
    21.           case "bye"   : $this->send($user->socket,"bye");                               break;  
    22.           //default      : $this->send($user->socket,$msg." not understood");              break;  
    23.           default      : $this->sendAll($user, $msg);              break;  
    24.         }  
    25.     }  
    26.     function sendAll($currentUser, $msg){  
    27.         $usersList = $this->users;  
    28.         foreach ($usersList as $user){  
    29.           if ($user !== $currentUser) // 自己发送的消息就不再接收一次了  
    30.             $this->send($user->socket, $msg);  
    31.         }  
    32.     }  
    33. }  
    34. $master = new ChatBot("localhost",12345);  

    客户端代码:

    <html>

    <head>

    <title>WebSocket</title>

    <style>

     html,body{font:normal 0.9em arial,helvetica;}

     #log {440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}

     #msg {330px;}

    </style>

    <script>

    var socket;

    function init(){

      var host = "ws://localhost:12345/websocket/server.php";

      try{

        socket = new WebSocket(host);

        log('WebSocket - status '+socket.readyState);

        socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };

        socket.onmessage = function(msg){ log("Received: "+msg.data); };

        socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };

      }

      catch(ex){ log(ex); }

      $("msg").focus();

    }

    function send(){

      var txt,msg;

      txt = $("msg");

      msg = txt.value;

      if(!msg){ alert("Message can not be empty"); return; }

      txt.value="";

      txt.focus();

      try{ socket.send(msg); log('Sent: '+msg); } catch(ex){ log(ex); }

    }

    function quit(){

      log("Goodbye!");

      socket.close();

      socket=null;

    }

    // Utilities

    function $(id){ return document.getElementById(id); }

    function log(msg){ $("log").innerHTML+="<br>"+msg; }

    function onkey(event){ if(event.keyCode==13){ send(); } }

    </script>

    </head>

    <body onload="init()">

     <h3>WebSocket v2.00</h3>

     <div id="log"></div>

     <input id="msg" type="textbox" onkeypress="onkey(event)"/>

     <button onclick="send()">Send</button>

     <button onclick="quit()">Quit</button>

     <div>Commands: hello, hi, name, age, date, time, thanks, bye</div>

    </body>

    </html>

    PS:

    *  这个websocket的类文件可能有一点问题,客户端握手后应该接收的第一条信息都丢失了,没细看代码,以后再检查吧。

  • 相关阅读:
    数据持久化
    计算机中的上下文
    URL
    MVC之Control中使用AOP
    富客户端
    一些术语的解释
    docker mysql 安装
    用C#开发Windows服务
    java 图片文件Base64编码与二进制编码格式互相转换
    Camera打开前置摄像头或后置摄像头
  • 原文地址:https://www.cnblogs.com/liangxiaofeng/p/5206456.html
Copyright © 2020-2023  润新知