├── .gitignore ├── README.md └── server.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebSocketDemo-php 2 | 基于 WebSocket 的聊天室(后端代码) 3 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 'system', 'message' => $ip . ' connected'))); 36 | send_message($response); 37 | $found_socket = array_search($socket, $changed); 38 | unset($changed[$found_socket]); 39 | } 40 | 41 | //轮询 每个client socket 连接 42 | foreach ($changed as $changed_socket) { 43 | 44 | //如果有client数据发送过来 45 | while (socket_recv($changed_socket, $buf, 1024, 0) >= 1) { 46 | //解码发送过来的数据 47 | $received_text = unmask($buf); 48 | $tst_msg = json_decode($received_text); 49 | $user_name = $tst_msg->name; 50 | $user_message = $tst_msg->message; 51 | 52 | //把消息发送回所有连接的 client 上去 53 | $response_text = mask(json_encode(array('type' => 'usermsg', 'name' => $user_name, 'message' => $user_message))); 54 | send_message($response_text); 55 | break 2; 56 | } 57 | 58 | //检查offline的client 59 | $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ); 60 | if ($buf === false) { 61 | $found_socket = array_search($changed_socket, $clients); 62 | socket_getpeername($changed_socket, $ip); 63 | unset($clients[$found_socket]); 64 | $response = mask(json_encode(array('type' => 'system', 'message' => $ip . ' disconnected'))); 65 | send_message($response); 66 | } 67 | } 68 | } 69 | // 关闭监听的socket 70 | socket_close($sock); 71 | 72 | //发送消息的方法 73 | function send_message($msg) 74 | { 75 | global $clients; 76 | foreach ($clients as $changed_socket) { 77 | @socket_write($changed_socket, $msg, strlen($msg)); 78 | } 79 | return true; 80 | } 81 | 82 | 83 | //解码数据 84 | function unmask($text) 85 | { 86 | $length = ord($text[1]) & 127; 87 | if ($length == 126) { 88 | $masks = substr($text, 4, 4); 89 | $data = substr($text, 8); 90 | } elseif ($length == 127) { 91 | $masks = substr($text, 10, 4); 92 | $data = substr($text, 14); 93 | } else { 94 | $masks = substr($text, 2, 4); 95 | $data = substr($text, 6); 96 | } 97 | $text = ""; 98 | for ($i = 0; $i < strlen($data); ++$i) { 99 | $text .= $data[$i] ^ $masks[$i % 4]; 100 | } 101 | return $text; 102 | } 103 | 104 | //编码数据 105 | function mask($text) 106 | { 107 | $b1 = 0x80 | (0x1 & 0x0f); 108 | $length = strlen($text); 109 | 110 | if ($length <= 125) 111 | $header = pack('CC', $b1, $length); 112 | elseif ($length > 125 && $length < 65536) 113 | $header = pack('CCn', $b1, 126, $length); 114 | elseif ($length >= 65536) 115 | $header = pack('CCNN', $b1, 127, $length); 116 | return $header . $text; 117 | } 118 | 119 | //握手的逻辑 120 | function perform_handshaking($receved_header, $client_conn, $host, $port) 121 | { 122 | $headers = array(); 123 | $lines = preg_split("/\r\n/", $receved_header); 124 | foreach ($lines as $line) { 125 | $line = chop($line); 126 | if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { 127 | $headers[$matches[1]] = $matches[2]; 128 | } 129 | } 130 | 131 | $secKey = $headers['Sec-WebSocket-Key']; 132 | $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); 133 | $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . 134 | "Upgrade: websocket\r\n" . 135 | "Connection: Upgrade\r\n" . 136 | "WebSocket-Origin: $host\r\n" . 137 | "WebSocket-Location: ws://$host:$port/demo/shout.php\r\n" . 138 | "Sec-WebSocket-Accept:$secAccept\r\n\r\n"; 139 | socket_write($client_conn, $upgrade, strlen($upgrade)); 140 | } --------------------------------------------------------------------------------