notice:
通過命令行執行php文件 如 php -q c:\path\server.php
通過本地web服務器訪問 http://127.0.0.1/websocket/index.php即可
notice:
需要php5.3或以上的執行環境,和一個web服務器如apache
浏覽器需支持html5 web socket
這裡監聽 socket端口 9505,如遇到端口被占用可能需要在這兩個文件內修改端口或者殺死相應端口進程
頁面手機上看起來比pc上好看!
1.客戶端代碼 html文件
1.client code: <!DOCTYPE html> <html> <head> <title>chatdemo</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, user-scalable=no"> <link href="https://cdn.bootcss.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> <!-- html, body { min-height: 100%; } body { margin: 0; padding: 0; width: 100%; font-family: "Microsoft Yahei",sans-serif, Arial; } .container { text-align: center; } .title { font-size: 16px; color: rgba(0, 0, 0, 0.3); position: fixed; line-height: 30px; height: 30px; left: 0px; right: 0px; background-color: white; } .content { background-color: #f1f1f1; border-top-left-radius: 6px; border-top-right-radius: 6px; margin-top: 30px; } .content .show-area { text-align: left; padding-top: 8px; padding-bottom: 168px; } .content .show-area .message { width: 70%; padding: 5px; word-wrap: break-word; word-break: normal; } .content .write-area { position: fixed; bottom: 0px; right: 0px; left: 0px; background-color: #f1f1f1; z-index: 10; width: 100%; height: 160px; border-top: 1px solid #d8d8d8; } .content .write-area .send { position: relative; top: -28px; height: 28px; border-top-left-radius: 55px; border-top-right-radius: 55px; } .content .write-area #name{ position: relative; top: -20px; line-height: 28px; font-size: 13px; } --> </style> </head> <body> <div class="container"> <div class="title">簡易聊天demo</div> <div class="content"> <div class="show-area"></div> <div class="write-area"> <div><button class="btn btn-default send" >發送</button></div> <div><input name="name" id="name" type="text" placeholder="input your name"></div> <div> <textarea name="message" id="message" cols="38" rows="4" placeholder="input your message..."></textarea> </div> </div> </div> </div> <script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.2/js/bootstrap.min.js"></script> <script> $(function(){ var wsurl = 'ws://127.0.0.1:9505/websocket/server.php'; var websocket; var i = 0; if(window.WebSocket){ websocket = new WebSocket(wsurl); //連接建立 websocket.onopen = function(evevt){ console.log("Connected to WebSocket server."); $('.show-area').append('<p class="bg-info message"><i class="glyphicon glyphicon-info-sign"></i>Connected to WebSocket server!</p>'); } //收到消息 websocket.onmessage = function(event) { var msg = JSON.parse(event.data); //解析收到的json消息數據 var type = msg.type; // 消息類型 var umsg = msg.message; //消息文本 var uname = msg.name; //發送人 i++; if(type == 'usermsg'){ $('.show-area').append('<p class="bg-success message"><i class="glyphicon glyphicon-user"></i><a name="'+i+'"></a><span class="label label-primary">'+uname+' say: </span>'+umsg+'</p>'); } if(type == 'system'){ $('.show-area').append('<p class="bg-warning message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>'+umsg+'</p>'); } $('#message').val(''); window.location.hash = '#'+i; } //發生錯誤 websocket.onerror = function(event){ i++; console.log("Connected to WebSocket server error"); $('.show-area').append('<p class="bg-danger message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>Connect to WebSocket server error.</p>'); window.location.hash = '#'+i; } //連接關閉 websocket.onclose = function(event){ i++; console.log('websocket Connection Closed. '); $('.show-area').append('<p class="bg-warning message"><a name="'+i+'"></a><i class="glyphicon glyphicon-info-sign"></i>websocket Connection Closed.</p>'); window.location.hash = '#'+i; } function send(){ var name = $('#name').val(); var message = $('#message').val(); if(!name){ alert('請輸入用戶名!'); return false; } if(!message){ alert('發送消息不能為空!'); return false; } var msg = { message: message, name: name }; try{ websocket.send(JSON.stringify(msg)); } catch(ex) { console.log(ex); } } //按下enter鍵發送消息 $(window).keydown(function(event){ if(event.keyCode == 13){ console.log('user enter'); send(); } }); //點發送按鈕發送消息 $('.send').bind('click',function(){ send(); }); } else{ alert('該浏覽器不支持web socket'); } }); </script> </body> </html> View Code
2.socket服務器端代碼 php文件
2.php code: <?php $host = '127.0.0.1'; $port = '9505'; $null = NULL; //創建tcp socket $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); socket_bind($socket, 0, $port); //監聽端口 socket_listen($socket); //連接的client socket 列表 $clients = array($socket); //設置一個死循環,用來監聽連接 ,狀態 while (true) { $changed = $clients; socket_select($changed, $null, $null, 0, 10); //如果有新的連接 if (in_array($socket, $changed)) { //接受並加入新的socket連接 $socket_new = socket_accept($socket); $clients[] = $socket_new; //通過socket獲取數據執行handshake $header = socket_read($socket_new, 1024); perform_handshaking($header, $socket_new, $host, $port); //獲取client ip 編碼json數據,並發送通知 socket_getpeername($socket_new, $ip); $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' connected'))); send_message($response); $found_socket = array_search($socket, $changed); unset($changed[$found_socket]); } //輪詢 每個client socket 連接 foreach ($changed as $changed_socket) { //如果有client數據發送過來 while(socket_recv($changed_socket, $buf, 1024, 0) >= 1) { //解碼發送過來的數據 $received_text = unmask($buf); $tst_msg = json_decode($received_text); $user_name = $tst_msg->name; $user_message = $tst_msg->message; //把消息發送回所有連接的 client 上去 $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message))); send_message($response_text); break 2; } //檢查offline的client $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ); if ($buf === false) { $found_socket = array_search($changed_socket, $clients); socket_getpeername($changed_socket, $ip); unset($clients[$found_socket]); $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnected'))); send_message($response); } } } // 關閉監聽的socket socket_close($sock); //發送消息的方法 function send_message($msg) { global $clients; foreach($clients as $changed_socket) { @socket_write($changed_socket,$msg,strlen($msg)); } return true; } //解碼數據 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; } //編碼數據 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; } //握手的邏輯 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'))); $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)); } View Code
以後有空再整份java版本的