websocket服务器+客户端

  1 <?php
  2 $demo = new ws('192.168.90.47',12345);
  3 $demo->run();
  4 
  5 
  6 class ws
  7 {
  8     //当前服务端主连接
  9     private $currentFirstSocket;
 10     //存放客户端socket连接
 11     private $socketList = array();
 12     //存放自定义的客户端连接
 13     private $clientList = array();
 14 
 15 
 16     public function __construct($address, $port)
 17     {
 18         $this->currentFirstSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
 19         socket_set_option($this->currentFirstSocket, SOL_SOCKET, SO_REUSEADDR, 1);
 20         socket_bind($this->currentFirstSocket, $address, $port);
 21         socket_listen($this->currentFirstSocket,2);
 22 
 23         $this->socketList[] = $this->currentFirstSocket;
 24         self::notice('Server Started ...');
 25         self::notice('Listening on   : ' . $address . ' port ' . $port);
 26     }
 27 
 28 
 29     public function run()
 30     {
 31         while (TRUE) {
 32             $connect = $this->socketList;
 33             $write = NULL;
 34             $except = NULL;
 35             self::notice(count($this->socketList) . "  wait to connect ....");
 36             socket_select($connect, $write, $except, NULL);
 37 
 38             foreach ($connect as $currentConnect) {
 39                 self::notice($currentConnect . " into connect");
 40 
 41                 //第一次连接
 42                 if ($currentConnect === $this->currentFirstSocket) {
 43                     $connectSuccessHandle = socket_accept($this->currentFirstSocket);
 44                     $this->socketList[] = $connectSuccessHandle;
 45                     $this->clientList[] = array(
 46                         "handshake" => false,
 47                         "clientHandle" => $connectSuccessHandle
 48                     );
 49                     self::notice($connectSuccessHandle. " new connect success ...");
 50                 } else {
 51                     $currentConnectKey = $this->searchSocketByHandle($currentConnect);
 52                     //第二次接收到的是客户端发送的websocket头协议
 53                     $len = @socket_recv($currentConnect, $buffer, 2048, 0);
 54                     self::notice("len ".$len);
 55                     if ($len < 10) {    //如果接收到的信息长度小于10当作客户端退出
 56                         $this->socketExit($currentConnectKey);
 57                         self::notice($currentConnect . " exit" . "  have:" . count($this->socketList));
 58                         break;
 59                     }
 60                     //检查是否握手,去握手
 61                     if ($this->clientList[$currentConnectKey]['handshake'] === false) {
 62                         self::notice($currentConnect." handshake success...");
 63                         $this->handshake($currentConnectKey, $buffer);
 64                     } else {
 65                         //正式连接,解包客户端的信息
 66                         $buffer = $this->unmask($buffer);
 67                         self::notice($currentConnect." send to server...");
 68 
 69                         $this->send($buffer);
 70                     }
 71                 }
 72             }
 73         }
 74     }
 75 
 76     /*
 77      * 握手
 78      * */
 79     private function handshake($currentConnectKey, $buffer)
 80     {
 81         $buf = substr($buffer, strpos($buffer, 'Sec-WebSocket-Key:') + 18);
 82         $key = trim(substr($buf, 0, strpos($buf, "
")));
 83         $new_key = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
 84         $new_message = "HTTP/1.1 101 Switching Protocols
";
 85         $new_message .= "Upgrade: websocket
";
 86         $new_message .= "Sec-WebSocket-Version: 13
";
 87         $new_message .= "Connection: Upgrade
";
 88         $new_message .= "Sec-WebSocket-Accept: " . $new_key . "

";
 89 
 90         socket_write($this->clientList[$currentConnectKey]['clientHandle'], $new_message, strlen($new_message));
 91         $this->clientList[$currentConnectKey]['handshake'] = true;
 92     }
 93 
 94     /*
 95      * 客户端退出处理
 96      * */
 97     private function socketExit($currentConnectKey)
 98     {
 99         $currentSocket = $this->clientList[$currentConnectKey]['clientHandle'];
100         socket_shutdown($currentSocket);
101         socket_close($currentSocket);
102         foreach ($this->socketList as $key => $val) {
103             if ($val === $currentSocket) {
104                 self::notice("delete " . $this->socketList[$key]);
105                 unset($this->socketList[$key]);
106             }
107         }
108         unset($this->clientList[$currentConnectKey]);
109     }
110 
111     /*
112      * 发送信息给所有客户端
113      * */
114     private function send($sendMsg)
115     {
116         $sendMsg = $this->mask($sendMsg);
117         foreach ($this->clientList as $val) {
118             self::notice("write to " . $val['clientHandle'] . " " . $sendMsg);
119             socket_write($val['clientHandle'], $sendMsg, strlen($sendMsg));
120         }
121     }
122 
123     /*
124      * 解包客户端发送的信息
125      * */
126     function unmask($text)
127     {
128         $length = ord($text[1]) & 127;
129         if ($length == 126) {
130             $masks = substr($text, 4, 4);
131             $data = substr($text, 8);
132         } elseif ($length == 127) {
133             $masks = substr($text, 10, 4);
134             $data = substr($text, 14);
135         } else {
136             $masks = substr($text, 2, 4);
137             $data = substr($text, 6);
138         }
139         $text = "";
140         for ($i = 0; $i < strlen($data); ++$i) {
141             $text .= $data[$i] ^ $masks[$i % 4];
142         }
143         return $text;
144     }
145     /*
146      * 封包信息发送给客户端
147      * */
148     function mask($text)
149     {
150         $b1 = 0x80 | (0x1 & 0x0f);
151         $length = strlen($text);
152 
153         if ($length <= 125)
154             $header = pack('CC', $b1, $length);
155         elseif ($length > 125 && $length < 65536)
156             $header = pack('CCn', $b1, 126, $length);
157         elseif ($length >= 65536)
158             $header = pack('CCNN', $b1, 127, $length);
159         return $header . $text;
160     }
161 
162     private function searchSocketByHandle($handle)
163     {
164         foreach ($this->clientList as $key => $value) {
165             if ($value['clientHandle'] === $handle) {
166                 return $key;
167             }
168         }
169         return false;
170     }
171 
172     private static function notice($message, $num = 1)
173     {
174         echo date("H:i:s") . "  " . $message;
175         echo str_repeat("
", $num);
176     }
177 }

客户端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>client</title>
    <script src="bootstrap/js/jq.js"></script>
    <script>
        var socket;
        $(document).ready(function(e) {
            connect();
        });


        function connect(){
            var url='ws://192.168.174.132:12345';
            socket = new WebSocket(url);
            socket.onopen=function(){
                if(socket.readyState==1){
                    socket.send("hello this is html");
                }else{
                    console.log("login fail");
                }
            }
            socket.onmessage=function(msg){
                console.log(msg);
            }

            socket.onclose=function(){
                console.log("close");
            }
        }

        function sendMsg()
        {
            var contetent = $("#test").val();
            console.log("send: "+contetent);
            socket.send(contetent);
        }


    </script>
</head>
<body>

<input type="input" id="test">
<input type="button" onclick="sendMsg()" value="send">

</body>
</html>
原文地址:https://www.cnblogs.com/hanyouchun/p/4921256.html